@usableapp/cardds 0.7.10 → 0.7.12
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 +22 -0
- package/css/sheet.css +15 -4
- package/dist/cardds.css +15 -4
- package/dist/sheets/SheetSteps.d.ts +3 -1
- package/dist/sheets/SheetSteps.js +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
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.12 — 2026-09-25
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- `SheetSteps`: each step's title line is a THIN quiet bar — 32px (`--tap-base` × 2/3), an ink tint, rounded, 4px apart;
|
|
9
|
+
the tap stays 44px. Opening a step never makes its bar taller: the step's content shows on the ground under it. Replaces
|
|
10
|
+
the rule on top (0.7.11's `--sheet-step-border-w` default is now 0). With `numbered`, the number sits in a white disc on the bar
|
|
11
|
+
(was the brand colour). Knobs `--sheet-steps-row-h` · `--sheet-steps-gap` · `--sheet-step-bar-bg` · `-bar-ink` ·
|
|
12
|
+
`-bar-radius` · `-bar-pad-x` · `--sheet-step-num-bg` · `-num-ink`.
|
|
13
|
+
- The old ruled look is knobs only (story `SheetSteps · Ruled`): `--sheet-step-bar-bg: transparent; --sheet-step-bar-radius: 0;
|
|
14
|
+
--sheet-step-bar-pad-x: 0; --sheet-steps-row-h: var(--tap-sm); --sheet-step-border-w: 1.5px; --sheet-steps-gap: 0;
|
|
15
|
+
--sheet-step-num-bg: var(--ink-tint)`.
|
|
16
|
+
|
|
17
|
+
## 0.7.11 — 2026-09-25
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `SheetSteps numbered`: each step's number in a disc before its title, every disc the brand colour (`--accent` /
|
|
21
|
+
`--on-accent`). Default off — titles only, as before. Knobs `--sheet-step-num-size` · `--sheet-step-num-font` ·
|
|
22
|
+
`--sheet-step-num-bg` · `--sheet-step-num-ink`. Story `SheetSteps · Numbered`.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- The rule over each step is thinner: 2px (`--rule-w`) → 1px. Knob `--sheet-step-border-w`.
|
|
26
|
+
|
|
5
27
|
## 0.7.10 — 2026-09-25
|
|
6
28
|
|
|
7
29
|
### Fixed
|
package/css/sheet.css
CHANGED
|
@@ -388,7 +388,6 @@
|
|
|
388
388
|
.sheet-step {
|
|
389
389
|
position: relative; min-width: 0;
|
|
390
390
|
display: grid; grid-template-rows: auto 0fr; align-content: start; /* mid-fold the rows sum to less than the box: the spare room stays BELOW, never spread into the title row (Lh: it looked like a spring) */
|
|
391
|
-
border-top: var(--rule-w) solid var(--sheet-step-border, var(--hairline));
|
|
392
391
|
flex: 0 0 auto;
|
|
393
392
|
transition: grid-template-rows var(--motion-sheet) ease, flex-grow var(--motion-sheet) ease;
|
|
394
393
|
}
|
|
@@ -396,16 +395,28 @@
|
|
|
396
395
|
in one beat the step that was open folds while the new one unfolds (flex-grow and the 1fr row both ease), the piles slide
|
|
397
396
|
with them, nothing jumps (Lh 2026-09-24) */
|
|
398
397
|
.sheet-step--now { grid-template-rows: auto 1fr; flex-grow: 1; }
|
|
399
|
-
.sheet-
|
|
398
|
+
.sheet-step + .sheet-step { margin-top: var(--sheet-steps-gap, var(--sp-1)); } /* the folded bars read as a pile, a little ground between them (Lh 2026-09-25) */
|
|
399
|
+
.sheet-step__head { /* every step's title line is a quiet BAR (a tint of the ink, not the brand — a brand bar read as a button, Lh 2026-09-25), the same height open or folded — opening shows the white room under it, never a taller bar (Lh 2026-09-25; was a thin rule on top) */
|
|
400
|
+
display: flex; align-items: center; gap: var(--sp-3); min-height: var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)); /* THIN: 32px, a summary line, not a button (Lh 2026-09-25) — the tap stays 44, see __go */
|
|
401
|
+
padding-inline: var(--sheet-step-bar-pad-x, var(--sp-3)); border-radius: var(--sheet-step-bar-radius, var(--r-chip));
|
|
402
|
+
background: var(--sheet-step-bar-bg, var(--ink-tint)); color: var(--sheet-step-bar-ink, inherit);
|
|
403
|
+
border-top: var(--sheet-step-border-w, 0px) solid var(--sheet-step-border, currentColor); /* a rule instead of the bar: set this, the bar's bg transparent, radius 0 */
|
|
404
|
+
}
|
|
405
|
+
.sheet-step__summary { color: inherit; opacity: 0.8; }
|
|
400
406
|
.sheet-step__title { font: var(--sheet-step-title-font, var(--type-label)); margin: 0; flex: 0 1 auto; min-width: 0; outline: none; /* the label size, not a body title: a pile of rows must stay small (Lh 2026-09-24) */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
407
|
+
.sheet-step__num { /* `numbered`: the step's number in a disc BEFORE its title (Lh 2026-09-25) — a white disc on the bar, the number in the ink */
|
|
408
|
+
flex: none; display: grid; place-items: center; inline-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2)); block-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2));
|
|
409
|
+
border-radius: 50%; font: var(--sheet-step-num-font, var(--type-caption)); font-variant-numeric: tabular-nums;
|
|
410
|
+
background: var(--sheet-step-num-bg, var(--card-bg)); color: var(--sheet-step-num-ink, inherit);
|
|
411
|
+
}
|
|
401
412
|
.sheet-step__summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
402
413
|
.sheet-step__check { margin-inline-start: auto; }
|
|
403
414
|
.sheet-step__body { min-height: 0; overflow: clip; overflow-clip-margin: var(--sp-1); }
|
|
404
|
-
.sheet-step__inner { display: grid; gap: var(--card-gap); padding-
|
|
415
|
+
.sheet-step__inner { display: grid; gap: var(--card-gap); padding-block: var(--card-gap); }
|
|
405
416
|
.sheet-step__inner > * { min-width: 0; }
|
|
406
417
|
.sheet-step__inner > .link { justify-self: center; } /* a link of its own row in a step (skip, "change number", "set PIN again"): centred under the step, as the skip (Lh 2026-09-25) */
|
|
407
418
|
.sheet-step:not(.sheet-step--now) > .sheet-step__body { visibility: hidden; transition: visibility 0s linear var(--motion-sheet); }
|
|
408
|
-
.sheet-step__go { position: absolute; inset: 0; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
|
419
|
+
.sheet-step__go { position: absolute; inset: min(0px, (var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)) - var(--tap-sm)) / 2) 0 auto; min-height: max(100%, var(--tap-sm)); /* a thin bar still takes a 44px tap */ margin: 0; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
|
409
420
|
/* the last step's finish, when the app gives one: under the steps */
|
|
410
421
|
.sheet-steps__finish { display: grid; padding-top: var(--card-gap); }
|
|
411
422
|
@media (prefers-reduced-motion: reduce) { .sheet-step { transition: none; } }
|
package/dist/cardds.css
CHANGED
|
@@ -1576,7 +1576,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1576
1576
|
.sheet-step {
|
|
1577
1577
|
position: relative; min-width: 0;
|
|
1578
1578
|
display: grid; grid-template-rows: auto 0fr; align-content: start; /* mid-fold the rows sum to less than the box: the spare room stays BELOW, never spread into the title row (Lh: it looked like a spring) */
|
|
1579
|
-
border-top: var(--rule-w) solid var(--sheet-step-border, var(--hairline));
|
|
1580
1579
|
flex: 0 0 auto;
|
|
1581
1580
|
transition: grid-template-rows var(--motion-sheet) ease, flex-grow var(--motion-sheet) ease;
|
|
1582
1581
|
}
|
|
@@ -1584,16 +1583,28 @@ button { font: inherit; cursor: pointer; }
|
|
|
1584
1583
|
in one beat the step that was open folds while the new one unfolds (flex-grow and the 1fr row both ease), the piles slide
|
|
1585
1584
|
with them, nothing jumps (Lh 2026-09-24) */
|
|
1586
1585
|
.sheet-step--now { grid-template-rows: auto 1fr; flex-grow: 1; }
|
|
1587
|
-
.sheet-
|
|
1586
|
+
.sheet-step + .sheet-step { margin-top: var(--sheet-steps-gap, var(--sp-1)); } /* the folded bars read as a pile, a little ground between them (Lh 2026-09-25) */
|
|
1587
|
+
.sheet-step__head { /* every step's title line is a quiet BAR (a tint of the ink, not the brand — a brand bar read as a button, Lh 2026-09-25), the same height open or folded — opening shows the white room under it, never a taller bar (Lh 2026-09-25; was a thin rule on top) */
|
|
1588
|
+
display: flex; align-items: center; gap: var(--sp-3); min-height: var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)); /* THIN: 32px, a summary line, not a button (Lh 2026-09-25) — the tap stays 44, see __go */
|
|
1589
|
+
padding-inline: var(--sheet-step-bar-pad-x, var(--sp-3)); border-radius: var(--sheet-step-bar-radius, var(--r-chip));
|
|
1590
|
+
background: var(--sheet-step-bar-bg, var(--ink-tint)); color: var(--sheet-step-bar-ink, inherit);
|
|
1591
|
+
border-top: var(--sheet-step-border-w, 0px) solid var(--sheet-step-border, currentColor); /* a rule instead of the bar: set this, the bar's bg transparent, radius 0 */
|
|
1592
|
+
}
|
|
1593
|
+
.sheet-step__summary { color: inherit; opacity: 0.8; }
|
|
1588
1594
|
.sheet-step__title { font: var(--sheet-step-title-font, var(--type-label)); margin: 0; flex: 0 1 auto; min-width: 0; outline: none; /* the label size, not a body title: a pile of rows must stay small (Lh 2026-09-24) */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1595
|
+
.sheet-step__num { /* `numbered`: the step's number in a disc BEFORE its title (Lh 2026-09-25) — a white disc on the bar, the number in the ink */
|
|
1596
|
+
flex: none; display: grid; place-items: center; inline-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2)); block-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2));
|
|
1597
|
+
border-radius: 50%; font: var(--sheet-step-num-font, var(--type-caption)); font-variant-numeric: tabular-nums;
|
|
1598
|
+
background: var(--sheet-step-num-bg, var(--card-bg)); color: var(--sheet-step-num-ink, inherit);
|
|
1599
|
+
}
|
|
1589
1600
|
.sheet-step__summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1590
1601
|
.sheet-step__check { margin-inline-start: auto; }
|
|
1591
1602
|
.sheet-step__body { min-height: 0; overflow: clip; overflow-clip-margin: var(--sp-1); }
|
|
1592
|
-
.sheet-step__inner { display: grid; gap: var(--card-gap); padding-
|
|
1603
|
+
.sheet-step__inner { display: grid; gap: var(--card-gap); padding-block: var(--card-gap); }
|
|
1593
1604
|
.sheet-step__inner > * { min-width: 0; }
|
|
1594
1605
|
.sheet-step__inner > .link { justify-self: center; } /* a link of its own row in a step (skip, "change number", "set PIN again"): centred under the step, as the skip (Lh 2026-09-25) */
|
|
1595
1606
|
.sheet-step:not(.sheet-step--now) > .sheet-step__body { visibility: hidden; transition: visibility 0s linear var(--motion-sheet); }
|
|
1596
|
-
.sheet-step__go { position: absolute; inset: 0; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
|
1607
|
+
.sheet-step__go { position: absolute; inset: min(0px, (var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)) - var(--tap-sm)) / 2) 0 auto; min-height: max(100%, var(--tap-sm)); /* a thin bar still takes a 44px tap */ margin: 0; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
|
1597
1608
|
/* the last step's finish, when the app gives one: under the steps */
|
|
1598
1609
|
.sheet-steps__finish { display: grid; padding-top: var(--card-gap); }
|
|
1599
1610
|
@media (prefers-reduced-motion: reduce) { .sheet-step { transition: none; } }
|
|
@@ -22,6 +22,8 @@ export interface SheetStepsProps extends ComponentProps<'div'> {
|
|
|
22
22
|
/** true: the member may go back and forth — a tap on a done or next step opens it, an up/down swipe moves one step.
|
|
23
23
|
* false (default): only forward — the done steps stay in the pile, never opened again (a swipe up passes only a `skippable` step). */
|
|
24
24
|
reversible?: boolean;
|
|
25
|
+
/** true: each step's number in a disc before its title — a white disc on the step's bar (Lh 2026-09-25). Default: titles only. */
|
|
26
|
+
numbered?: boolean;
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* SheetSteps — one task taken in steps INSIDE one sheet (phone · code · PIN · PIN again · face · notices). The done steps pile up
|
|
@@ -30,7 +32,7 @@ export interface SheetStepsProps extends ComponentProps<'div'> {
|
|
|
30
32
|
* steps on the last one. Goes in a `SheetBody`.
|
|
31
33
|
* The sheet may be dragged down to peek and back — the body keeps where it was scrolled.
|
|
32
34
|
*/
|
|
33
|
-
export declare function SheetSteps({ at, reached, defaultAt, onAtChange, reversible, finish, className, style, children, onFocus, onBlur, onPointerDown, onPointerUp, onPointerCancel, ...rest }: SheetStepsProps): import("react").JSX.Element;
|
|
35
|
+
export declare function SheetSteps({ at, reached, defaultAt, onAtChange, reversible, numbered, finish, className, style, children, onFocus, onBlur, onPointerDown, onPointerUp, onPointerCancel, ...rest }: SheetStepsProps): import("react").JSX.Element;
|
|
34
36
|
export interface SheetStepProps extends Omit<ComponentProps<'li'>, 'title'> {
|
|
35
37
|
/** the step's name — always shown: over the open step, on the folded line of a done one */
|
|
36
38
|
title: ReactNode;
|
|
@@ -16,7 +16,7 @@ const StepCtx = createContext(null);
|
|
|
16
16
|
* steps on the last one. Goes in a `SheetBody`.
|
|
17
17
|
* The sheet may be dragged down to peek and back — the body keeps where it was scrolled.
|
|
18
18
|
*/
|
|
19
|
-
export function SheetSteps({ at, reached, defaultAt = 0, onAtChange, reversible = false, finish, className, style, children, onFocus, onBlur, onPointerDown, onPointerUp, onPointerCancel, ...rest }) {
|
|
19
|
+
export function SheetSteps({ at, reached, defaultAt = 0, onAtChange, reversible = false, numbered = false, finish, className, style, children, onFocus, onBlur, onPointerDown, onPointerUp, onPointerCancel, ...rest }) {
|
|
20
20
|
const [inner, setInner] = useState(defaultAt);
|
|
21
21
|
const current = at ?? inner;
|
|
22
22
|
const steps = Children.toArray(children).filter(isValidElement);
|
|
@@ -185,7 +185,7 @@ export function SheetSteps({ at, reached, defaultAt = 0, onAtChange, reversible
|
|
|
185
185
|
else if (dy < 0 && ref.current?.querySelector('.sheet-step--now[data-skippable]'))
|
|
186
186
|
go(to, 'skip'); // forward only, and only past a step that may be skipped
|
|
187
187
|
};
|
|
188
|
-
return (_jsxs("div", { ref: ref, className: cx('sheet-steps', !reversible && 'sheet-steps--locked', className), style: style, onFocus: focusIn, onBlur: focusOut, onPointerDown: down, onPointerUp: up, onPointerCancel: (e) => { onPointerCancel?.(e); press.current = null; }, ...rest, children: [_jsx("input", { ref: holderRef, className: "sheet-steps__holder", "aria-hidden": true, tabIndex: -1, autoComplete: "off", onInput: (e) => { e.currentTarget.value = ''; } }), _jsx("ol", { className: "sheet-steps__track", children: steps.map((step, i) => (_jsx(StepCtx.Provider, { value: { i, at: current, furthest: reach, count, reversible, go }, children: step }, step.key ?? i))) }), finish != null && ( // always in sight; it takes taps only once every step is done (or skipped)
|
|
188
|
+
return (_jsxs("div", { ref: ref, className: cx('sheet-steps', !reversible && 'sheet-steps--locked', className), style: style, onFocus: focusIn, onBlur: focusOut, onPointerDown: down, onPointerUp: up, onPointerCancel: (e) => { onPointerCancel?.(e); press.current = null; }, ...rest, children: [_jsx("input", { ref: holderRef, className: "sheet-steps__holder", "aria-hidden": true, tabIndex: -1, autoComplete: "off", onInput: (e) => { e.currentTarget.value = ''; } }), _jsx("ol", { className: "sheet-steps__track", children: steps.map((step, i) => (_jsx(StepCtx.Provider, { value: { numbered, i, at: current, furthest: reach, count, reversible, go }, children: step }, step.key ?? i))) }), finish != null && ( // always in sight; it takes taps only once every step is done (or skipped)
|
|
189
189
|
_jsx("div", { className: "sheet-steps__finish", children: isValidElement(finish) ? cloneElement(finish, { disabled: reach < count || finish.props.disabled }) : finish }))] }));
|
|
190
190
|
}
|
|
191
191
|
/** SheetStep — one step of a SheetSteps: its title, then what the member does (a Field, a Pin, a Toggle, the step's own Btn). */
|
|
@@ -193,7 +193,7 @@ export function SheetStep({ title, summary, skippable, skipLabel = 'Skip', error
|
|
|
193
193
|
const ctx = useContext(StepCtx);
|
|
194
194
|
if (!ctx)
|
|
195
195
|
throw new Error('SheetStep goes inside a SheetSteps');
|
|
196
|
-
const { i, at, furthest, count, reversible, go } = ctx;
|
|
196
|
+
const { numbered, i, at, furthest, count, reversible, go } = ctx;
|
|
197
197
|
const phase = i === at ? 'now' : i < furthest ? 'done' : 'next';
|
|
198
198
|
const off = phase !== 'now';
|
|
199
199
|
/* the step is the FORM GROUP of the format checks inside it (Field format/digits/required, Pin match): the words of the first
|
|
@@ -238,5 +238,5 @@ export function SheetStep({ title, summary, skippable, skipLabel = 'Skip', error
|
|
|
238
238
|
bad.focus();
|
|
239
239
|
}
|
|
240
240
|
};
|
|
241
|
-
return (_jsxs("li", { className: cx('sheet-step', `sheet-step--${phase}`, busy != null && busy !== false && 'sheet-step--busy', className), "aria-current": phase === 'now' ? 'step' : undefined, "aria-busy": busy != null && busy !== false ? true : undefined, "data-skippable": skippable || undefined, ...rest, children: [_jsxs("div", { className: "sheet-step__head", inert: off || undefined, children: [_jsx("h3", { className: "sheet-step__title", tabIndex: -1, children: title }), phase === 'done' && summary != null && _jsx("span", { className: "sheet-step__summary t-caption
|
|
241
|
+
return (_jsxs("li", { className: cx('sheet-step', `sheet-step--${phase}`, busy != null && busy !== false && 'sheet-step--busy', className), "aria-current": phase === 'now' ? 'step' : undefined, "aria-busy": busy != null && busy !== false ? true : undefined, "data-skippable": skippable || undefined, ...rest, children: [_jsxs("div", { className: "sheet-step__head", inert: off || undefined, children: [numbered && _jsx("span", { className: "sheet-step__num", "aria-hidden": true, children: i + 1 }), _jsx("h3", { className: "sheet-step__title", tabIndex: -1, children: title }), phase === 'done' && summary != null && _jsx("span", { className: "sheet-step__summary t-caption", children: summary }), phase === 'done' && _jsx(Icon, { name: "check", size: "xs", className: "sheet-step__check" })] }), _jsx("div", { className: "sheet-step__body", children: _jsxs("div", { className: "sheet-step__inner", inert: off || undefined, onClickCapture: press, onPointerDown: keepCursor, children: [_jsx(CheckContext.Provider, { value: group, children: children }), _jsx(FormNote, { error: error ?? note, busy: busy }), skippable && _jsx(Link, { sm: true, muted: true, className: "sheet-step__skip", onClick: () => go(i + 1, 'skip'), children: skipLabel })] }) }), reversible && off && (_jsx("button", { type: "button", className: "sheet-step__go", "aria-label": typeof title === 'string' ? title : `${i + 1} / ${count}`, onClick: () => go(i, 'tap') }))] }));
|
|
242
242
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usableapp/cardds",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.12",
|
|
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.",
|