@usableapp/cardds 0.7.0 → 0.7.1
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 +9 -0
- package/README.md +3 -11
- package/css/actions.css +10 -23
- package/css/step.css +2 -8
- package/dist/cardds.css +12 -31
- package/dist/scaffold/ActionBar.d.ts +2 -2
- package/dist/scaffold/ActionBar.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
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.1 — 2026-09-24
|
|
6
|
+
|
|
7
|
+
### The bar fixed from outside is gone
|
|
8
|
+
- Before: an `ActionBar` written after `</Screen>` (in `<body>` or an app shell) was fixed to the viewport, and the screen
|
|
9
|
+
/ step kept `--bar-reserve` of room for it. After: `.action-bar` is in flow; a bar is always the Screen's `bottom` (its
|
|
10
|
+
last child) — a layer over the content's foot in a fill screen, a sticky last row on a scrolling one (0.7.0). A bar left
|
|
11
|
+
outside a Screen now sits wherever it was written. `--bar-reserve` is still published (the centred card's cap reads it);
|
|
12
|
+
nothing reserves room with it any more. `ActionBar inline` stays, for a bare bar in a preview.
|
|
13
|
+
|
|
5
14
|
## 0.7.0 — 2026-09-24
|
|
6
15
|
|
|
7
16
|
Breaking — the clean-up promised since 0.6.5. Four renames/removals, one new way.
|
package/README.md
CHANGED
|
@@ -410,17 +410,9 @@ Demo: the *skeleton* block at the top of `elements.html`.
|
|
|
410
410
|
5. **`.screen` carries the horizontal inset and nothing vertical.** The inset
|
|
411
411
|
is `--screen-pad`; cards and on-surface UI sit flush inside it, with no
|
|
412
412
|
per-component side padding. Top and bottom are zero: vertical room only
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
parent hosts the bar counts (`:has(> .action-bar) .screen`): `<body>` or an
|
|
417
|
-
app shell alike. A page with no bars needs no override, and `.screen--fill`
|
|
418
|
-
is excluded from the bottom reserves — there a `.step` carries the bar's
|
|
419
|
-
room itself, as a bottom margin, reading the same `--bar-reserve`. The
|
|
420
|
-
reserves use the nominal control height, so a bar whose button wraps to two
|
|
421
|
-
lines can still overlap the last line of content.
|
|
422
|
-
**In a `.screen--fill` none of this applies to a bar in the screen** (2026-09-23): it is the column's last
|
|
423
|
-
part and the browser measures it — the reserves are the old way for a bar outside a fill screen.
|
|
413
|
+
is taken by what is in the column. The bottom bar is always the screen's last child (0.7.1 — the bar fixed to
|
|
414
|
+
the viewport from outside and its reserves are gone): in a `.screen--fill` a layer over the content's foot, on a
|
|
415
|
+
scrolling screen a `sticky` last row. `--bar-reserve` stays published only for a `.card--centre`'s cap.
|
|
424
416
|
**Padding is published, not copied.** `.screen` sets `--screen-pad` and
|
|
425
417
|
`--screen-gap`, its appbar reserve sets `--header-gap`, and `.card` sets
|
|
426
418
|
`--card-pad` and `--card-gap`; anything that has to cancel a container's
|
package/css/actions.css
CHANGED
|
@@ -216,21 +216,15 @@
|
|
|
216
216
|
opacity: 1;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
/* ---- bottom CTA pack:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
the
|
|
225
|
-
--kb is the on-screen keyboard's height, published on :root by cardds.js
|
|
226
|
-
from the visual viewport (0 without one, and on a platform that shrinks
|
|
227
|
-
the layout viewport itself): the bar rides above the keyboard on iOS the
|
|
228
|
-
way it already does on Android, and .step grows its room by the same. */
|
|
219
|
+
/* ---- bottom CTA pack: the screen's bottom (Screen `bottom`) ----
|
|
220
|
+
A bar is always INSIDE its .screen, its last child (0.7.1: the bar fixed to the viewport from outside, and the
|
|
221
|
+
reserves the screen and the step kept for it, are gone). In a .screen--fill it is a layer over the content's foot
|
|
222
|
+
(step.css); on a scrolling screen a sticky last row (below). --bar-reserve stays published — the bar's nominal
|
|
223
|
+
height — for the one rule that still needs it without measuring: a .card--centre's cap (card.css).
|
|
224
|
+
--kb is the on-screen keyboard's height (cardds.js): the bar rides above it. */
|
|
229
225
|
:root { --bar-reserve: calc(var(--btn-h) + var(--sp-3) + var(--sp-5) + var(--sp-6)); }
|
|
230
226
|
.action-bar {
|
|
231
|
-
position:
|
|
232
|
-
inset-inline: 0;
|
|
233
|
-
bottom: var(--kb, 0);
|
|
227
|
+
position: relative;
|
|
234
228
|
z-index: 30; /* above .sheet-stack sheets, which paint at --level 1-5 */
|
|
235
229
|
max-width: var(--screen-w);
|
|
236
230
|
margin-inline: auto;
|
|
@@ -241,9 +235,9 @@
|
|
|
241
235
|
/* a bar INSIDE a scrolling (not fill) screen (Lh 2026-09-24, 0.7.0): the screen is a column at least one phone tall, the bar its
|
|
242
236
|
last row — at the foot when the words are short, and sticky to the viewport's foot (above the keyboard) while they scroll;
|
|
243
237
|
it takes the room it needs in the column, so nothing is added to the content for it */
|
|
244
|
-
.screen:not(.screen--fill):has(> .action-bar) { display: flex; flex-direction: column; min-height: 100dvh; }
|
|
245
|
-
.screen:not(.screen--fill) > .action-bar {
|
|
246
|
-
position: sticky;
|
|
238
|
+
.screen:not(.screen--fill):has(> .action-bar:last-child) { display: flex; flex-direction: column; min-height: 100dvh; }
|
|
239
|
+
.screen:not(.screen--fill) > .action-bar:last-child {
|
|
240
|
+
position: sticky; bottom: var(--kb, 0rem);
|
|
247
241
|
max-width: none;
|
|
248
242
|
margin: auto calc(-1 * var(--screen-pad)) 0;
|
|
249
243
|
}
|
|
@@ -313,13 +307,6 @@
|
|
|
313
307
|
:is(.action-bar--pager, .action-bar__tier--pager) > :not(.icon-btn):not(:first-child):not(:last-child) { margin-inline: auto; }
|
|
314
308
|
:is(.action-bar--pager, .action-bar__tier--pager):has(> :nth-child(3)) > .icon-btn:first-child { margin-right: 0; }
|
|
315
309
|
|
|
316
|
-
/* .screen's default bottom padding is a constant; derive the reserve from the
|
|
317
|
-
bar instead, so a taller button (a longer label, a script with more leading)
|
|
318
|
-
can never end up underneath it. Any parent that hosts the bar counts —
|
|
319
|
-
<body>, or an app shell (#app > .screen + .action-bar) — since the bar is
|
|
320
|
-
fixed to the viewport wherever it sits. (.screen--fill reserves nothing:
|
|
321
|
-
its .step carries the bar's room itself, step.css.) */
|
|
322
|
-
:has(> .action-bar) .screen:not(.screen--fill) { padding-bottom: var(--bar-reserve); }
|
|
323
310
|
|
|
324
311
|
/* ---- action note (Lh 2026-09-24): the main button's place, the main button's size, but ON the ground — no fill, no border, no
|
|
325
312
|
rounding — one line of muted words, an inline icon allowed; nothing to press. In a CardFoot or an ActionBar row it takes the
|
package/css/step.css
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<section class="page">…the words…</section> ← first
|
|
11
11
|
<article class="card sheet sheet--half">…the controls… <div class="card__foot">…</div></article> ← last, optional
|
|
12
12
|
</section>
|
|
13
|
-
<div class="action-bar">…</div> ← only when there is no sheet
|
|
13
|
+
<div class="action-bar">…</div> ← the screen's last child, only when there is no sheet
|
|
14
14
|
|
|
15
15
|
The step is the box the sheet slides in. It fills the screen
|
|
16
16
|
column between the top bar's reserve (.screen already pads for
|
|
@@ -59,11 +59,6 @@
|
|
|
59
59
|
more air and a short one less; a screen with a bar keeps the bar's own room */
|
|
60
60
|
.screen > .step:first-child, .screen--lead > .step { --_lead: var(--screen-lead, 15cqh); } /* top="lead" says it; a Step first says it too */
|
|
61
61
|
|
|
62
|
-
/* the bar's room — read from actions.css, never recomputed. Margin, not
|
|
63
|
-
padding, so the clip cuts at the box edge and nothing shows behind the
|
|
64
|
-
bar. Any parent that hosts the bar counts — <body>, an app shell, a demo
|
|
65
|
-
frame — the bar is fixed to the viewport wherever it sits. */
|
|
66
|
-
:has(> .action-bar) .step { margin-bottom: calc(var(--bar-reserve) + var(--kb, 0rem)); }
|
|
67
62
|
/* a walk (the dots float on its foot): the reading block runs right to the bar's top edge — the reserve's breathing gap (--sp-6)
|
|
68
63
|
is given back, the dots keep the words clear of the bar (Lh 2026-09-23) */
|
|
69
64
|
|
|
@@ -119,7 +114,7 @@
|
|
|
119
114
|
TOP + CONTENT in one flex column; BOTTOM is a LAYER over them (Lh 2026-09-23, second call — replaces "the bar is a row"):
|
|
120
115
|
in a .screen--fill the content runs to the screen's bottom edge, and the bar lies over it at the foot — it covers what is
|
|
121
116
|
under it, on purpose; nothing is added to the content to make room. The keyboard lifts the layer (--kb).
|
|
122
|
-
(
|
|
117
|
+
(On a scrolling screen the bar is a sticky last row instead, actions.css.)
|
|
123
118
|
============================================================ */
|
|
124
119
|
.screen--fill > .action-bar {
|
|
125
120
|
position: absolute; inset-inline: 0; top: auto; bottom: var(--kb, 0rem);
|
|
@@ -127,7 +122,6 @@
|
|
|
127
122
|
margin: 0;
|
|
128
123
|
padding-top: 0; /* no ground of its own, never a solid strip */
|
|
129
124
|
}
|
|
130
|
-
.screen--fill:has(> .action-bar) > .step { margin-bottom: 0; }
|
|
131
125
|
/* the walk's dots are the BOTTOM layer's too (Lh 2026-09-23): a row of the bar, over its buttons, centred */
|
|
132
126
|
.action-bar > .pager__at, .action-bar__tier > .pager__at { align-self: center; }
|
|
133
127
|
/* the TOP is in the column too (Lh 2026-09-23): an .appbar in a fill screen is its first row, not fixed over it — the screen
|
package/dist/cardds.css
CHANGED
|
@@ -1543,7 +1543,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1543
1543
|
<section class="page">…the words…</section> ← first
|
|
1544
1544
|
<article class="card sheet sheet--half">…the controls… <div class="card__foot">…</div></article> ← last, optional
|
|
1545
1545
|
</section>
|
|
1546
|
-
<div class="action-bar">…</div> ← only when there is no sheet
|
|
1546
|
+
<div class="action-bar">…</div> ← the screen's last child, only when there is no sheet
|
|
1547
1547
|
|
|
1548
1548
|
The step is the box the sheet slides in. It fills the screen
|
|
1549
1549
|
column between the top bar's reserve (.screen already pads for
|
|
@@ -1592,11 +1592,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1592
1592
|
more air and a short one less; a screen with a bar keeps the bar's own room */
|
|
1593
1593
|
.screen > .step:first-child, .screen--lead > .step { --_lead: var(--screen-lead, 15cqh); } /* top="lead" says it; a Step first says it too */
|
|
1594
1594
|
|
|
1595
|
-
/* the bar's room — read from actions.css, never recomputed. Margin, not
|
|
1596
|
-
padding, so the clip cuts at the box edge and nothing shows behind the
|
|
1597
|
-
bar. Any parent that hosts the bar counts — <body>, an app shell, a demo
|
|
1598
|
-
frame — the bar is fixed to the viewport wherever it sits. */
|
|
1599
|
-
:has(> .action-bar) .step { margin-bottom: calc(var(--bar-reserve) + var(--kb, 0rem)); }
|
|
1600
1595
|
/* a walk (the dots float on its foot): the reading block runs right to the bar's top edge — the reserve's breathing gap (--sp-6)
|
|
1601
1596
|
is given back, the dots keep the words clear of the bar (Lh 2026-09-23) */
|
|
1602
1597
|
|
|
@@ -1652,7 +1647,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1652
1647
|
TOP + CONTENT in one flex column; BOTTOM is a LAYER over them (Lh 2026-09-23, second call — replaces "the bar is a row"):
|
|
1653
1648
|
in a .screen--fill the content runs to the screen's bottom edge, and the bar lies over it at the foot — it covers what is
|
|
1654
1649
|
under it, on purpose; nothing is added to the content to make room. The keyboard lifts the layer (--kb).
|
|
1655
|
-
(
|
|
1650
|
+
(On a scrolling screen the bar is a sticky last row instead, actions.css.)
|
|
1656
1651
|
============================================================ */
|
|
1657
1652
|
.screen--fill > .action-bar {
|
|
1658
1653
|
position: absolute; inset-inline: 0; top: auto; bottom: var(--kb, 0rem);
|
|
@@ -1660,7 +1655,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1660
1655
|
margin: 0;
|
|
1661
1656
|
padding-top: 0; /* no ground of its own, never a solid strip */
|
|
1662
1657
|
}
|
|
1663
|
-
.screen--fill:has(> .action-bar) > .step { margin-bottom: 0; }
|
|
1664
1658
|
/* the walk's dots are the BOTTOM layer's too (Lh 2026-09-23): a row of the bar, over its buttons, centred */
|
|
1665
1659
|
.action-bar > .pager__at, .action-bar__tier > .pager__at { align-self: center; }
|
|
1666
1660
|
/* the TOP is in the column too (Lh 2026-09-23): an .appbar in a fill screen is its first row, not fixed over it — the screen
|
|
@@ -2048,21 +2042,15 @@ button { font: inherit; cursor: pointer; }
|
|
|
2048
2042
|
opacity: 1;
|
|
2049
2043
|
}
|
|
2050
2044
|
|
|
2051
|
-
/* ---- bottom CTA pack:
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
the
|
|
2057
|
-
--kb is the on-screen keyboard's height, published on :root by cardds.js
|
|
2058
|
-
from the visual viewport (0 without one, and on a platform that shrinks
|
|
2059
|
-
the layout viewport itself): the bar rides above the keyboard on iOS the
|
|
2060
|
-
way it already does on Android, and .step grows its room by the same. */
|
|
2045
|
+
/* ---- bottom CTA pack: the screen's bottom (Screen `bottom`) ----
|
|
2046
|
+
A bar is always INSIDE its .screen, its last child (0.7.1: the bar fixed to the viewport from outside, and the
|
|
2047
|
+
reserves the screen and the step kept for it, are gone). In a .screen--fill it is a layer over the content's foot
|
|
2048
|
+
(step.css); on a scrolling screen a sticky last row (below). --bar-reserve stays published — the bar's nominal
|
|
2049
|
+
height — for the one rule that still needs it without measuring: a .card--centre's cap (card.css).
|
|
2050
|
+
--kb is the on-screen keyboard's height (cardds.js): the bar rides above it. */
|
|
2061
2051
|
:root { --bar-reserve: calc(var(--btn-h) + var(--sp-3) + var(--sp-5) + var(--sp-6)); }
|
|
2062
2052
|
.action-bar {
|
|
2063
|
-
position:
|
|
2064
|
-
inset-inline: 0;
|
|
2065
|
-
bottom: var(--kb, 0);
|
|
2053
|
+
position: relative;
|
|
2066
2054
|
z-index: 30; /* above .sheet-stack sheets, which paint at --level 1-5 */
|
|
2067
2055
|
max-width: var(--screen-w);
|
|
2068
2056
|
margin-inline: auto;
|
|
@@ -2073,9 +2061,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
2073
2061
|
/* a bar INSIDE a scrolling (not fill) screen (Lh 2026-09-24, 0.7.0): the screen is a column at least one phone tall, the bar its
|
|
2074
2062
|
last row — at the foot when the words are short, and sticky to the viewport's foot (above the keyboard) while they scroll;
|
|
2075
2063
|
it takes the room it needs in the column, so nothing is added to the content for it */
|
|
2076
|
-
.screen:not(.screen--fill):has(> .action-bar) { display: flex; flex-direction: column; min-height: 100dvh; }
|
|
2077
|
-
.screen:not(.screen--fill) > .action-bar {
|
|
2078
|
-
position: sticky;
|
|
2064
|
+
.screen:not(.screen--fill):has(> .action-bar:last-child) { display: flex; flex-direction: column; min-height: 100dvh; }
|
|
2065
|
+
.screen:not(.screen--fill) > .action-bar:last-child {
|
|
2066
|
+
position: sticky; bottom: var(--kb, 0rem);
|
|
2079
2067
|
max-width: none;
|
|
2080
2068
|
margin: auto calc(-1 * var(--screen-pad)) 0;
|
|
2081
2069
|
}
|
|
@@ -2145,13 +2133,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
2145
2133
|
:is(.action-bar--pager, .action-bar__tier--pager) > :not(.icon-btn):not(:first-child):not(:last-child) { margin-inline: auto; }
|
|
2146
2134
|
:is(.action-bar--pager, .action-bar__tier--pager):has(> :nth-child(3)) > .icon-btn:first-child { margin-right: 0; }
|
|
2147
2135
|
|
|
2148
|
-
/* .screen's default bottom padding is a constant; derive the reserve from the
|
|
2149
|
-
bar instead, so a taller button (a longer label, a script with more leading)
|
|
2150
|
-
can never end up underneath it. Any parent that hosts the bar counts —
|
|
2151
|
-
<body>, or an app shell (#app > .screen + .action-bar) — since the bar is
|
|
2152
|
-
fixed to the viewport wherever it sits. (.screen--fill reserves nothing:
|
|
2153
|
-
its .step carries the bar's room itself, step.css.) */
|
|
2154
|
-
:has(> .action-bar) .screen:not(.screen--fill) { padding-bottom: var(--bar-reserve); }
|
|
2155
2136
|
|
|
2156
2137
|
/* ---- action note (Lh 2026-09-24): the main button's place, the main button's size, but ON the ground — no fill, no border, no
|
|
2157
2138
|
rounding — one line of muted words, an inline icon allowed; nothing to press. In a CardFoot or an ActionBar row it takes the
|
|
@@ -2,7 +2,7 @@ import type { ComponentProps } from 'react';
|
|
|
2
2
|
export interface ActionBarProps extends ComponentProps<'div'> {
|
|
3
3
|
/** `pager`: previous / next as two equal buttons at the edges (a Link may sit between them; the PagerAt dots never do — they go under the card) · `tiers`: rows stacked — a control tier above the buttons */
|
|
4
4
|
variant?: 'pager' | 'tiers';
|
|
5
|
-
/**
|
|
5
|
+
/** a bare bar for a preview: no position, full width, no side padding (a real bar is a Screen's `bottom`) */
|
|
6
6
|
inline?: boolean;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
@@ -11,7 +11,7 @@ export interface ActionBarProps extends ComponentProps<'div'> {
|
|
|
11
11
|
* Plain: one wide primary Btn (the step that commits). `pager`: Back + Next as peers — buttons only; where you are is the
|
|
12
12
|
* PagerAt under the card in a Centre group, never in the bar (Lh, 2026-09-12).
|
|
13
13
|
* `tiers`: an ActionBarTier with a control (a Slider) above the button tier.
|
|
14
|
-
* In a `Screen fill`
|
|
14
|
+
* In a `Screen fill` it is a layer over the content's foot; on a scrolling Screen a sticky last row.
|
|
15
15
|
*/
|
|
16
16
|
export declare function ActionBar({ variant, inline, className, style, ...rest }: ActionBarProps): import("react").JSX.Element;
|
|
17
17
|
export interface ActionBarTierProps extends ComponentProps<'div'> {
|
|
@@ -6,7 +6,7 @@ import { cx } from '../cx.js';
|
|
|
6
6
|
* Plain: one wide primary Btn (the step that commits). `pager`: Back + Next as peers — buttons only; where you are is the
|
|
7
7
|
* PagerAt under the card in a Centre group, never in the bar (Lh, 2026-09-12).
|
|
8
8
|
* `tiers`: an ActionBarTier with a control (a Slider) above the button tier.
|
|
9
|
-
* In a `Screen fill`
|
|
9
|
+
* In a `Screen fill` it is a layer over the content's foot; on a scrolling Screen a sticky last row.
|
|
10
10
|
*/
|
|
11
11
|
export function ActionBar({ variant, inline, className, style, ...rest }) {
|
|
12
12
|
return (_jsx("div", { className: cx('action-bar', variant && `action-bar--${variant}`, className), style: inline ? { position: 'static', width: '100%', maxWidth: 'none', paddingInline: 0, ...style } : style, ...rest }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usableapp/cardds",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
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.",
|