@usableapp/cardds 0.6.0 → 0.6.2
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 +12 -0
- package/css/base.css +4 -0
- package/css/card.css +5 -1
- package/css/media.css +1 -1
- package/css/step.css +19 -0
- package/css/tokens.css +2 -1
- package/dist/cardds.css +31 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +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.2 — 2026-09-23
|
|
6
|
+
|
|
7
|
+
- **The picture stays put, only the words scroll** (Lh 2026-09-23): in a `Step`, a `BaseContent` whose FIRST child is a picture (`img` / `Placeholder`) keeps it sticky at the top of the reading block, and the heading and words scroll under it. The picture is capped at 40% of the step's height (`min(--base-content-media-h, --step-media-share)`, new knob `--step-media-share`, default `40cqh`), so on a short phone or at a large text size the picture gives way, never the words. Nothing to change in an app.
|
|
8
|
+
- A larger opening picture is a theme setting, not a code change: `:root { --base-content-media-h: 18.75rem; }` (300px; the 40% cap still applies inside a Step).
|
|
9
|
+
|
|
10
|
+
## 0.6.1 — 2026-09-22
|
|
11
|
+
|
|
12
|
+
- **A screen with no bar** (Lh 2026-09-22): a `Step` that is the `Screen`'s first child starts its words 15% of the screen's height down — `--screen-lead` (a knob, `15cqh`; `Screen fill` is a size container, `--_lead` is registered so the share resolves against the screen). Story `Step · NoBar`.
|
|
13
|
+
- **The base's own h1**: `CardHead level="h1"` inside a `BaseContent` reads at the card heading's size (`--fs-h2`) — h1 is the semantic level; `--fs-h1` stays the bar title's size.
|
|
14
|
+
|
|
5
15
|
## 0.6.0 — 2026-09-22
|
|
6
16
|
|
|
17
|
+
Also in 0.6.0 (same commit): `slideTo(direction, update, scope?)` names the moving page for the move only — CSS no longer names every walk's current page (two walks on one page aborted the transition); on a base page the picture comes FIRST, then the heading, then the words; `--card-3-muted` is a real colour role (the five tone-3 opacity rules in lists.css read it; default = the wireframe's old look); `--dot-drop` (0.09em) sits a signal dot on the letters' middle.
|
|
18
|
+
|
|
7
19
|
### The walk's dots are the Step's, not each page's (Lh 2026-09-22) — BREAKING
|
|
8
20
|
Reverses 0.5.2. A walk of `BaseContent` pages now carries **one** `PagerAt`, written after `Pages` — not one inside every page. `.step > .pager__at` sits at the base's foot, above the bar; `.base-content:has(> .pager__at:last-child)` (the 0.5.2 per-page flex-column form) is gone. An app with a `PagerAt` at the end of each page's `BaseContent` should move it to a single `PagerAt` after the `Pages` block instead.
|
|
9
21
|
|
package/css/base.css
CHANGED
|
@@ -78,6 +78,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
78
78
|
height: 100dvh;
|
|
79
79
|
overflow: clip;
|
|
80
80
|
position: relative; /* the box a .card--centre or a .screen__centre floats in (card.css) */
|
|
81
|
+
container-type: size; /* a share of the SCREEN's height (a no-bar step's lead, step.css) reads it as cqh — a real phone's height, or the gallery's frame */
|
|
81
82
|
}
|
|
82
83
|
/* min-height: 0 — a flex item's floor is its content, and the stack's content
|
|
83
84
|
is its own full-height sheets, so without it the stack grew to the whole
|
|
@@ -118,6 +119,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
118
119
|
.base-content > .t-body { color: var(--base-content-body-ink, var(--on-surface)); max-width: 34ch; } /* full ink: a sentence is read, muted is for meta (Lh 2026-09-22 — the words on the base were grey and hard to read) */
|
|
119
120
|
/* an illustration among the words (Lh 2026-09-22): capped, so a tall picture never pushes the words under the bar;
|
|
120
121
|
(the walk's dots are NOT here — they are the Step's, after its Pages: step.css, Lh 2026-09-22) */
|
|
122
|
+
/* the page's ONE heading on the base (a screen with no TopBar: CardHead level="h1") reads at the card heading's size —
|
|
123
|
+
h1 is the semantic level, not a bigger face; --fs-h1 is the bar title's size (Lh 2026-09-22) */
|
|
124
|
+
.base-content > .card__head > .t-h1 { font-size: var(--fs-h2); }
|
|
121
125
|
.base-content > :is(img, .ph) { max-height: var(--base-content-media-h, 14rem); width: auto; height: auto; max-width: 100%; object-fit: contain; justify-self: start; align-self: start; } /* height auto: an <img> with width/height attributes keeps its ratio inside the cap — no band under the picture */
|
|
122
126
|
.base-content > .ph { width: 100%; height: var(--base-content-media-h, 14rem); }
|
|
123
127
|
.base-content .cta-pack {
|
package/css/card.css
CHANGED
|
@@ -222,7 +222,11 @@
|
|
|
222
222
|
<article class="card" aria-current="step">…</article>
|
|
223
223
|
<article class="card" inert>…</article>
|
|
224
224
|
</div><div class="pager__at">…</div></div> */
|
|
225
|
-
.pages {
|
|
225
|
+
.pages {
|
|
226
|
+
display: grid;
|
|
227
|
+
user-select: none; /* a walk is dragged, not read-and-selected — without this, a finger crossing the words highlights them instead of turning the page (timebank #59) */
|
|
228
|
+
touch-action: pan-y; /* leaves vertical scroll to the browser; horizontal is the app's own drag, never a native pan */
|
|
229
|
+
}
|
|
226
230
|
.pages > :is(.card, .base-content) { grid-area: 1 / 1; align-content: start; } /* stretched to the cell, the words stay at the top */
|
|
227
231
|
.pages > [inert] { visibility: hidden; }
|
|
228
232
|
/* pages of WORDS (Lh, 2026-09-20): the same walk with .base-content pages, on the base — in a .step the
|
package/css/media.css
CHANGED
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
border-color: transparent;
|
|
117
117
|
border-bottom-right-radius: 0;
|
|
118
118
|
background-image: linear-gradient(to top left,
|
|
119
|
-
var(--
|
|
119
|
+
var(--card-bg) 0 50%, /* the card's own background, not --surface (the page's) — a mismatch here drew a seam at the flap's edge */
|
|
120
120
|
color-mix(in srgb, currentColor 18%, var(--card-bg)) 50% 100%);
|
|
121
121
|
background-repeat: no-repeat;
|
|
122
122
|
background-position: bottom right;
|
package/css/step.css
CHANGED
|
@@ -54,6 +54,10 @@
|
|
|
54
54
|
that thing. */
|
|
55
55
|
.topbar + .step { margin-top: calc(-1 * var(--screen-gap)); --_lead: var(--screen-gap); }
|
|
56
56
|
.appbar + .step { margin-top: calc(-1 * var(--header-gap)); --_lead: var(--screen-gap); }
|
|
57
|
+
/* no bar at all — the step is the screen's first child (a page whose one heading is the base's own, Lh 2026-09-22):
|
|
58
|
+
the words start a share of the screen down — --screen-lead, 15% of its height (Lh 2026-09-22), so a tall phone gives
|
|
59
|
+
more air and a short one less; a screen with a bar keeps the bar's own room */
|
|
60
|
+
.screen > .step:first-child { --_lead: var(--screen-lead, 15cqh); }
|
|
57
61
|
|
|
58
62
|
/* the bar's room — read from actions.css, never recomputed. Margin, not
|
|
59
63
|
padding, so the clip cuts at the box edge and nothing shows behind the
|
|
@@ -98,3 +102,18 @@
|
|
|
98
102
|
overflow-y: auto;
|
|
99
103
|
overscroll-behavior: contain;
|
|
100
104
|
}
|
|
105
|
+
|
|
106
|
+
/* a picture that opens the words stays put, only the words scroll under it (Lh 2026-09-23, timebank's walk at large text:
|
|
107
|
+
the picture scrolled away with the words). Sticky at the top of the reading block's scroller, on the base's ground so the
|
|
108
|
+
words pass under it; capped at a share of the STEP (a size container) so the words always keep more than half the column —
|
|
109
|
+
the picture gives way on a short phone or at a big text size, never the words. */
|
|
110
|
+
.step > .base-content > :is(img, .ph):first-child,
|
|
111
|
+
.step > .pages > .base-content > :is(img, .ph):first-child {
|
|
112
|
+
position: sticky;
|
|
113
|
+
top: 0;
|
|
114
|
+
z-index: 1;
|
|
115
|
+
background-color: var(--surface); /* the words pass UNDER it, never through a transparent picture */
|
|
116
|
+
max-height: min(var(--base-content-media-h, 14rem), var(--step-media-share, 40cqh));
|
|
117
|
+
}
|
|
118
|
+
.step > .base-content > .ph:first-child,
|
|
119
|
+
.step > .pages > .base-content > .ph:first-child { height: min(var(--base-content-media-h, 14rem), var(--step-media-share, 40cqh)); }
|
package/css/tokens.css
CHANGED
|
@@ -211,6 +211,7 @@
|
|
|
211
211
|
falls back to when its own declaration cannot resolve.
|
|
212
212
|
============================================================ */
|
|
213
213
|
@property --sheet-peek { syntax: "<length>"; inherits: true; initial-value: 0px; }
|
|
214
|
-
@property --sheet-head-h { syntax: "<length>"; inherits: true; initial-value: 48px; }
|
|
214
|
+
@property --sheet-head-h { syntax: "<length>"; inherits: true; initial-value: 48px; }
|
|
215
|
+
@property --_lead { syntax: "<length>"; inherits: true; initial-value: 0px; } /* the room above a step's words: registered so a cqh share (a no-bar step, 15% of the SCREEN) resolves where it is declared — on the step, against the screen — not on the words, against the step */ /* the sheet head's floor (sheet.css) — registered so the Sheet reads it resolved */
|
|
215
216
|
@property --sheet-half { syntax: "<number>"; inherits: true; initial-value: 0.5; }
|
|
216
217
|
@property --sheet-3q { syntax: "<number>"; inherits: true; initial-value: 0.25; }
|
package/dist/cardds.css
CHANGED
|
@@ -252,7 +252,8 @@
|
|
|
252
252
|
falls back to when its own declaration cannot resolve.
|
|
253
253
|
============================================================ */
|
|
254
254
|
@property --sheet-peek { syntax: "<length>"; inherits: true; initial-value: 0px; }
|
|
255
|
-
@property --sheet-head-h { syntax: "<length>"; inherits: true; initial-value: 48px; }
|
|
255
|
+
@property --sheet-head-h { syntax: "<length>"; inherits: true; initial-value: 48px; }
|
|
256
|
+
@property --_lead { syntax: "<length>"; inherits: true; initial-value: 0px; } /* the room above a step's words: registered so a cqh share (a no-bar step, 15% of the SCREEN) resolves where it is declared — on the step, against the screen — not on the words, against the step */ /* the sheet head's floor (sheet.css) — registered so the Sheet reads it resolved */
|
|
256
257
|
@property --sheet-half { syntax: "<number>"; inherits: true; initial-value: 0.5; }
|
|
257
258
|
@property --sheet-3q { syntax: "<number>"; inherits: true; initial-value: 0.25; }
|
|
258
259
|
|
|
@@ -456,6 +457,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
456
457
|
height: 100dvh;
|
|
457
458
|
overflow: clip;
|
|
458
459
|
position: relative; /* the box a .card--centre or a .screen__centre floats in (card.css) */
|
|
460
|
+
container-type: size; /* a share of the SCREEN's height (a no-bar step's lead, step.css) reads it as cqh — a real phone's height, or the gallery's frame */
|
|
459
461
|
}
|
|
460
462
|
/* min-height: 0 — a flex item's floor is its content, and the stack's content
|
|
461
463
|
is its own full-height sheets, so without it the stack grew to the whole
|
|
@@ -496,6 +498,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
496
498
|
.base-content > .t-body { color: var(--base-content-body-ink, var(--on-surface)); max-width: 34ch; } /* full ink: a sentence is read, muted is for meta (Lh 2026-09-22 — the words on the base were grey and hard to read) */
|
|
497
499
|
/* an illustration among the words (Lh 2026-09-22): capped, so a tall picture never pushes the words under the bar;
|
|
498
500
|
(the walk's dots are NOT here — they are the Step's, after its Pages: step.css, Lh 2026-09-22) */
|
|
501
|
+
/* the page's ONE heading on the base (a screen with no TopBar: CardHead level="h1") reads at the card heading's size —
|
|
502
|
+
h1 is the semantic level, not a bigger face; --fs-h1 is the bar title's size (Lh 2026-09-22) */
|
|
503
|
+
.base-content > .card__head > .t-h1 { font-size: var(--fs-h2); }
|
|
499
504
|
.base-content > :is(img, .ph) { max-height: var(--base-content-media-h, 14rem); width: auto; height: auto; max-width: 100%; object-fit: contain; justify-self: start; align-self: start; } /* height auto: an <img> with width/height attributes keeps its ratio inside the cap — no band under the picture */
|
|
500
505
|
.base-content > .ph { width: 100%; height: var(--base-content-media-h, 14rem); }
|
|
501
506
|
.base-content .cta-pack {
|
|
@@ -745,7 +750,11 @@ button { font: inherit; cursor: pointer; }
|
|
|
745
750
|
<article class="card" aria-current="step">…</article>
|
|
746
751
|
<article class="card" inert>…</article>
|
|
747
752
|
</div><div class="pager__at">…</div></div> */
|
|
748
|
-
.pages {
|
|
753
|
+
.pages {
|
|
754
|
+
display: grid;
|
|
755
|
+
user-select: none; /* a walk is dragged, not read-and-selected — without this, a finger crossing the words highlights them instead of turning the page (timebank #59) */
|
|
756
|
+
touch-action: pan-y; /* leaves vertical scroll to the browser; horizontal is the app's own drag, never a native pan */
|
|
757
|
+
}
|
|
749
758
|
.pages > :is(.card, .base-content) { grid-area: 1 / 1; align-content: start; } /* stretched to the cell, the words stay at the top */
|
|
750
759
|
.pages > [inert] { visibility: hidden; }
|
|
751
760
|
/* pages of WORDS (Lh, 2026-09-20): the same walk with .base-content pages, on the base — in a .step the
|
|
@@ -1505,6 +1514,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
1505
1514
|
that thing. */
|
|
1506
1515
|
.topbar + .step { margin-top: calc(-1 * var(--screen-gap)); --_lead: var(--screen-gap); }
|
|
1507
1516
|
.appbar + .step { margin-top: calc(-1 * var(--header-gap)); --_lead: var(--screen-gap); }
|
|
1517
|
+
/* no bar at all — the step is the screen's first child (a page whose one heading is the base's own, Lh 2026-09-22):
|
|
1518
|
+
the words start a share of the screen down — --screen-lead, 15% of its height (Lh 2026-09-22), so a tall phone gives
|
|
1519
|
+
more air and a short one less; a screen with a bar keeps the bar's own room */
|
|
1520
|
+
.screen > .step:first-child { --_lead: var(--screen-lead, 15cqh); }
|
|
1508
1521
|
|
|
1509
1522
|
/* the bar's room — read from actions.css, never recomputed. Margin, not
|
|
1510
1523
|
padding, so the clip cuts at the box edge and nothing shows behind the
|
|
@@ -1550,6 +1563,21 @@ button { font: inherit; cursor: pointer; }
|
|
|
1550
1563
|
overscroll-behavior: contain;
|
|
1551
1564
|
}
|
|
1552
1565
|
|
|
1566
|
+
/* a picture that opens the words stays put, only the words scroll under it (Lh 2026-09-23, timebank's walk at large text:
|
|
1567
|
+
the picture scrolled away with the words). Sticky at the top of the reading block's scroller, on the base's ground so the
|
|
1568
|
+
words pass under it; capped at a share of the STEP (a size container) so the words always keep more than half the column —
|
|
1569
|
+
the picture gives way on a short phone or at a big text size, never the words. */
|
|
1570
|
+
.step > .base-content > :is(img, .ph):first-child,
|
|
1571
|
+
.step > .pages > .base-content > :is(img, .ph):first-child {
|
|
1572
|
+
position: sticky;
|
|
1573
|
+
top: 0;
|
|
1574
|
+
z-index: 1;
|
|
1575
|
+
background-color: var(--surface); /* the words pass UNDER it, never through a transparent picture */
|
|
1576
|
+
max-height: min(var(--base-content-media-h, 14rem), var(--step-media-share, 40cqh));
|
|
1577
|
+
}
|
|
1578
|
+
.step > .base-content > .ph:first-child,
|
|
1579
|
+
.step > .pages > .base-content > .ph:first-child { height: min(var(--base-content-media-h, 14rem), var(--step-media-share, 40cqh)); }
|
|
1580
|
+
|
|
1553
1581
|
/* ---- css/forms.css — outlined fields, add-row ---- */
|
|
1554
1582
|
/* ============================================================
|
|
1555
1583
|
cardds/forms.css — inputs + inline form actions
|
|
@@ -2969,7 +2997,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2969
2997
|
border-color: transparent;
|
|
2970
2998
|
border-bottom-right-radius: 0;
|
|
2971
2999
|
background-image: linear-gradient(to top left,
|
|
2972
|
-
var(--
|
|
3000
|
+
var(--card-bg) 0 50%, /* the card's own background, not --surface (the page's) — a mismatch here drew a seam at the flap's edge */
|
|
2973
3001
|
color-mix(in srgb, currentColor 18%, var(--card-bg)) 50% 100%);
|
|
2974
3002
|
background-repeat: no-repeat;
|
|
2975
3003
|
background-position: bottom right;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usableapp/cardds",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
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.",
|