@usableapp/cardds 0.7.10 → 0.7.11

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.11 — 2026-09-25
6
+
7
+ ### Added
8
+ - `SheetSteps numbered`: each step's number in a disc before its title, every disc the brand colour (`--accent` /
9
+ `--on-accent`). Default off — titles only, as before. Knobs `--sheet-step-num-size` · `--sheet-step-num-font` ·
10
+ `--sheet-step-num-bg` · `--sheet-step-num-ink`. Story `SheetSteps · Numbered`.
11
+
12
+ ### Changed
13
+ - The rule over each step is thinner: 2px (`--rule-w`) → 1px. Knob `--sheet-step-border-w`.
14
+
5
15
  ## 0.7.10 — 2026-09-25
6
16
 
7
17
  ### Fixed
package/css/sheet.css CHANGED
@@ -388,7 +388,7 @@
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));
391
+ border-top: var(--sheet-step-border-w, 1px) solid var(--sheet-step-border, var(--hairline)); /* a hair, not a rule: 2px read heavy between short rows (Lh 2026-09-25) */
392
392
  flex: 0 0 auto;
393
393
  transition: grid-template-rows var(--motion-sheet) ease, flex-grow var(--motion-sheet) ease;
394
394
  }
@@ -398,6 +398,11 @@
398
398
  .sheet-step--now { grid-template-rows: auto 1fr; flex-grow: 1; }
399
399
  .sheet-step__head { display: flex; align-items: center; gap: var(--sp-2); min-height: var(--sheet-steps-row-h, var(--tap-sm)); }
400
400
  .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; }
401
+ .sheet-step__num { /* `numbered`: the step's number in a disc BEFORE its title (Lh 2026-09-25) — every disc the same brand colour, none faded */
402
+ 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));
403
+ border-radius: 50%; font: var(--sheet-step-num-font, var(--type-caption)); font-variant-numeric: tabular-nums;
404
+ background: var(--sheet-step-num-bg, var(--accent)); color: var(--sheet-step-num-ink, var(--on-accent));
405
+ }
401
406
  .sheet-step__summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
402
407
  .sheet-step__check { margin-inline-start: auto; }
403
408
  .sheet-step__body { min-height: 0; overflow: clip; overflow-clip-margin: var(--sp-1); }
package/dist/cardds.css CHANGED
@@ -1576,7 +1576,7 @@ 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));
1579
+ border-top: var(--sheet-step-border-w, 1px) solid var(--sheet-step-border, var(--hairline)); /* a hair, not a rule: 2px read heavy between short rows (Lh 2026-09-25) */
1580
1580
  flex: 0 0 auto;
1581
1581
  transition: grid-template-rows var(--motion-sheet) ease, flex-grow var(--motion-sheet) ease;
1582
1582
  }
@@ -1586,6 +1586,11 @@ button { font: inherit; cursor: pointer; }
1586
1586
  .sheet-step--now { grid-template-rows: auto 1fr; flex-grow: 1; }
1587
1587
  .sheet-step__head { display: flex; align-items: center; gap: var(--sp-2); min-height: var(--sheet-steps-row-h, var(--tap-sm)); }
1588
1588
  .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; }
1589
+ .sheet-step__num { /* `numbered`: the step's number in a disc BEFORE its title (Lh 2026-09-25) — every disc the same brand colour, none faded */
1590
+ 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));
1591
+ border-radius: 50%; font: var(--sheet-step-num-font, var(--type-caption)); font-variant-numeric: tabular-nums;
1592
+ background: var(--sheet-step-num-bg, var(--accent)); color: var(--sheet-step-num-ink, var(--on-accent));
1593
+ }
1589
1594
  .sheet-step__summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1590
1595
  .sheet-step__check { margin-inline-start: auto; }
1591
1596
  .sheet-step__body { min-height: 0; overflow: clip; overflow-clip-margin: var(--sp-1); }
@@ -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 — all the same brand colour (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 t-muted", 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') }))] }));
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 t-muted", 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.10",
3
+ "version": "0.7.11",
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.",