@usableapp/cardds 0.6.10 → 0.6.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,20 @@
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.6.11 — 2026-09-23
6
+
7
+ ### A small link under a foot's buttons: `CardFoot link` (new, like an ActionBar's `ctaLink`)
8
+ - `CardFoot` has a new `link` slot: a `Link sm` on its own short row under the buttons, centred. It never shares the buttons'
9
+ line, and a sheet's foot strip grows by that row.
10
+ - `CallSheet` passes it as `sheet.footLink`.
11
+ - A `Link` put straight in a `CardFoot` is misuse: it takes a button's share and squeezes the button.
12
+
13
+ ### EmptyState, redone for the new anatomy
14
+ - `Screen top={TopBar} › Step › EmptyState`. It is a component in the content and no longer sits in a `Centre` over the
15
+ screen.
16
+ - Alone in a Step, it stands at the content's human centre (about 40% down).
17
+ - The TopBar names the page; the EmptyState heading says what is (not) there.
18
+
5
19
  ## 0.6.10 — 2026-09-23
6
20
 
7
21
  ### New template: `CallSheet` (layout-3)
package/css/card.css CHANGED
@@ -121,6 +121,10 @@
121
121
  .card__foot { display: flex; gap: var(--card-foot-gap, var(--sp-2)); align-items: center; }
122
122
  .card__foot > .btn { flex: 1 1 0; min-width: 0; }
123
123
  .card__foot:has(> :nth-child(3)) > .btn { padding-inline: var(--sp-2); }
124
+ /* a small link under the buttons (CardFoot link — Lh 2026-09-23, as the ActionBar's ctaLink): the row wraps, the link takes a
125
+ whole short row of its own, centred — it never shares the buttons' line */
126
+ .card__foot--link { flex-wrap: wrap; align-content: end; row-gap: var(--card-foot-link-gap, var(--sp-1)); }
127
+ .card__foot-link { flex: 1 0 100%; display: flex; justify-content: center; }
124
128
 
125
129
  /* media placeholder for wireframes — currentColor so it survives any palette */
126
130
  .ph {
package/css/sheet.css CHANGED
@@ -147,9 +147,12 @@
147
147
  stops that far above the sheet's edge. At peek there is only the handle and the title: the foot is gone.
148
148
  One row, ≤ 2 buttons. <div class="card__foot"><button class="btn btn--primary btn--block">…</button></div> */
149
149
  .sheet { --_foot: calc(var(--card-gap) + var(--btn-h) + var(--card-pad)); }
150
+ /* …and the link row under the button, when there is one (its line + the gap over it) */
151
+ .sheet:has(> .card__foot--link) { --_foot: calc(var(--card-gap) + var(--btn-h) + var(--sp-1) + calc(var(--tap-base) * 7 / 12) + var(--card-pad)); }
150
152
  .sheet > .card__foot {
151
153
  position: absolute; inset: calc(-1 * var(--border-w)) 0 0; z-index: 1; /* the sheet's border box: the same height the sheet's own % reads */
152
154
  align-items: end;
155
+ align-content: end;
153
156
  padding: 0 var(--card-pad) var(--card-pad);
154
157
  background: linear-gradient(var(--card-bg), var(--card-bg)) bottom / 100% var(--_foot) no-repeat;
155
158
  pointer-events: none;
package/css/step.css CHANGED
@@ -158,3 +158,8 @@
158
158
  border-top: 0;
159
159
  object-fit: cover;
160
160
  }
161
+ /* an EMPTY STATE is a component in the content box too (Lh 2026-09-23 — it no longer floats in a Centre over the screen): alone in a
162
+ Step it stands at the human centre of the content, 40% down (two parts of room above it, three below) */
163
+ .step:has(> .empty-state)::before { content: ""; flex: 2 1 0; }
164
+ .step:has(> .empty-state)::after { content: ""; flex: 3 1 0; }
165
+ .step > .empty-state { flex: none; }
package/dist/cardds.css CHANGED
@@ -653,6 +653,10 @@ button { font: inherit; cursor: pointer; }
653
653
  .card__foot { display: flex; gap: var(--card-foot-gap, var(--sp-2)); align-items: center; }
654
654
  .card__foot > .btn { flex: 1 1 0; min-width: 0; }
655
655
  .card__foot:has(> :nth-child(3)) > .btn { padding-inline: var(--sp-2); }
656
+ /* a small link under the buttons (CardFoot link — Lh 2026-09-23, as the ActionBar's ctaLink): the row wraps, the link takes a
657
+ whole short row of its own, centred — it never shares the buttons' line */
658
+ .card__foot--link { flex-wrap: wrap; align-content: end; row-gap: var(--card-foot-link-gap, var(--sp-1)); }
659
+ .card__foot-link { flex: 1 0 100%; display: flex; justify-content: center; }
656
660
 
657
661
  /* media placeholder for wireframes — currentColor so it survives any palette */
658
662
  .ph {
@@ -1301,9 +1305,12 @@ button { font: inherit; cursor: pointer; }
1301
1305
  stops that far above the sheet's edge. At peek there is only the handle and the title: the foot is gone.
1302
1306
  One row, ≤ 2 buttons. <div class="card__foot"><button class="btn btn--primary btn--block">…</button></div> */
1303
1307
  .sheet { --_foot: calc(var(--card-gap) + var(--btn-h) + var(--card-pad)); }
1308
+ /* …and the link row under the button, when there is one (its line + the gap over it) */
1309
+ .sheet:has(> .card__foot--link) { --_foot: calc(var(--card-gap) + var(--btn-h) + var(--sp-1) + calc(var(--tap-base) * 7 / 12) + var(--card-pad)); }
1304
1310
  .sheet > .card__foot {
1305
1311
  position: absolute; inset: calc(-1 * var(--border-w)) 0 0; z-index: 1; /* the sheet's border box: the same height the sheet's own % reads */
1306
1312
  align-items: end;
1313
+ align-content: end;
1307
1314
  padding: 0 var(--card-pad) var(--card-pad);
1308
1315
  background: linear-gradient(var(--card-bg), var(--card-bg)) bottom / 100% var(--_foot) no-repeat;
1309
1316
  pointer-events: none;
@@ -1684,6 +1691,11 @@ button { font: inherit; cursor: pointer; }
1684
1691
  border-top: 0;
1685
1692
  object-fit: cover;
1686
1693
  }
1694
+ /* an EMPTY STATE is a component in the content box too (Lh 2026-09-23 — it no longer floats in a Centre over the screen): alone in a
1695
+ Step it stands at the human centre of the content, 40% down (two parts of room above it, three below) */
1696
+ .step:has(> .empty-state)::before { content: ""; flex: 2 1 0; }
1697
+ .step:has(> .empty-state)::after { content: ""; flex: 3 1 0; }
1698
+ .step > .empty-state { flex: none; }
1687
1699
 
1688
1700
  /* ---- css/forms.css — outlined fields, add-row ---- */
1689
1701
  /* ============================================================
@@ -1,5 +1,9 @@
1
- import type { ComponentProps } from 'react';
1
+ import type { ComponentProps, ReactNode } from 'react';
2
2
  export interface CardFootProps extends ComponentProps<'div'> {
3
+ /** a SMALL LINK under the buttons, centred on its own short row (a `Link sm` — "details for developers") — the same as an
4
+ * ActionBar's ctaLink (Lh 2026-09-23). It never takes a share of the buttons' line */
5
+ link?: ReactNode;
3
6
  }
4
- /** CardFoot — the card's action buttons on one line: one takes the width, up to four share it evenly. Btns only. */
5
- export declare function CardFoot({ className, ...rest }: CardFootProps): import("react").JSX.Element;
7
+ /** CardFoot — the card's action buttons on one line: one takes the width, up to four share it evenly. Btns only in the row; a small
8
+ * link goes in `link`, under it. */
9
+ export declare function CardFoot({ link, className, children, ...rest }: CardFootProps): import("react").JSX.Element;
@@ -1,6 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cx } from '../cx.js';
3
- /** CardFoot — the card's action buttons on one line: one takes the width, up to four share it evenly. Btns only. */
4
- export function CardFoot({ className, ...rest }) {
5
- return _jsx("div", { className: cx('card__foot', className), ...rest });
3
+ /** CardFoot — the card's action buttons on one line: one takes the width, up to four share it evenly. Btns only in the row; a small
4
+ * link goes in `link`, under it. */
5
+ export function CardFoot({ link, className, children, ...rest }) {
6
+ return (_jsxs("div", { className: cx('card__foot', link != null && 'card__foot--link', className), ...rest, children: [children, link != null && _jsx("div", { className: "card__foot-link", children: link })] }));
6
7
  }
@@ -9,6 +9,6 @@ export interface EmptyStateProps extends Omit<ComponentProps<'div'>, 'title'> {
9
9
  /** the heading's level: h2 on a surface (default) · h3 inside a card's section, under its h3 heading */
10
10
  level?: 'h2' | 'h3';
11
11
  }
12
- /** EmptyState — centred: a large icon, a heading, a line of body text, an optional CTA. On the surface (always in a `Centre`)
12
+ /** EmptyState — centred: a large icon, a heading, a line of body text, an optional CTA. A component in the content: alone in a `Step` it stands at the content's human centre, 40% down (Lh 2026-09-23 — no longer a `Centre` over the screen)
13
13
  * or inside a card's section ("nothing here yet" under a heading) — the ink follows the card it sits in; full ink throughout, never muted (Lh 2026-09-16). */
14
14
  export declare function EmptyState({ icon, title, action, level, className, children, ...rest }: EmptyStateProps): 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
- /** EmptyState — centred: a large icon, a heading, a line of body text, an optional CTA. On the surface (always in a `Centre`)
3
+ /** EmptyState — centred: a large icon, a heading, a line of body text, an optional CTA. A component in the content: alone in a `Step` it stands at the content's human centre, 40% down (Lh 2026-09-23 — no longer a `Centre` over the screen)
4
4
  * or inside a card's section ("nothing here yet" under a heading) — the ink follows the card it sits in; full ink throughout, never muted (Lh 2026-09-16). */
5
5
  export function EmptyState({ icon, title, action, level = 'h2', className, children, ...rest }) {
6
6
  const H = level;
@@ -7,6 +7,8 @@ export interface CallSheetSheet {
7
7
  body: ReactNode | (() => ReactNode);
8
8
  /** the sheet foot: the one button that finishes the sheet */
9
9
  foot?: ReactNode;
10
+ /** a small link UNDER that button (a `Link sm`, e.g. details for developers) */
11
+ footLink?: ReactNode;
10
12
  }
11
13
  export interface CallSheetProps extends Omit<ComponentProps<typeof Screen>, 'children' | 'fill' | 'top' | 'bottom' | 'title'> {
12
14
  /** a picture above the words (optional) */
@@ -23,5 +23,5 @@ export function CallSheet({ media, heading, body, action, sheet, open: openProp,
23
23
  return (_jsxs(Screen, { fill: true, ...rest, children: [_jsxs(Step, { children: [_jsxs(Page, { children: [media, _jsx(CardHead, { level: "h1", heading: heading }), typeof body === 'string' ? _jsx(Text, { level: "body", children: body }) : body, _jsx(CtaPack, { children: _jsx(Btn, { primary: true, "aria-expanded": open, onClick: () => { setAt('full'); set(true); }, children: action }) })] }), _jsxs(Sheet, { state: open ? at : 'away', onStateChange: (s) => { if (s === 'away')
24
24
  set(false);
25
25
  else
26
- setAt(s); }, states: states, children: [_jsx(CardHead, { heading: sheet.title }), _jsx(SheetBody, { children: open ? (typeof sheet.body === 'function' ? sheet.body() : sheet.body) : null }), sheet.foot != null && _jsx(CardFoot, { children: sheet.foot })] })] }), children] }));
26
+ setAt(s); }, states: states, children: [_jsx(CardHead, { heading: sheet.title }), _jsx(SheetBody, { children: open ? (typeof sheet.body === 'function' ? sheet.body() : sheet.body) : null }), (sheet.foot != null || sheet.footLink != null) && _jsx(CardFoot, { link: sheet.footLink, children: sheet.foot })] })] }), children] }));
27
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usableapp/cardds",
3
- "version": "0.6.10",
3
+ "version": "0.6.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.",