@usableapp/cardds 0.5.0 → 0.5.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 +17 -0
- package/css/base.css +6 -1
- package/css/choice.css +15 -2
- package/css/forms.css +7 -4
- package/css/step.css +1 -0
- package/dist/cardds.css +29 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
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.5.2 — 2026-09-22
|
|
6
|
+
|
|
7
|
+
### Dots at the foot (Lh 2026-09-22)
|
|
8
|
+
A `BaseContent` whose last child is `PagerAt` now sits its dots at the bottom of the reading block, above the bar; the block fills the step for it. Nothing to change in an app.
|
|
9
|
+
|
|
10
|
+
## 0.5.1 — 2026-09-22
|
|
11
|
+
|
|
12
|
+
### Field type 26px, 56px tall (Lh 2026-09-22)
|
|
13
|
+
`--field-min-h` default is `--tap-lg` (56, was `--tap` 48) — every field stands as tall as the action field already did.
|
|
14
|
+
`--field-font` default is `400 var(--fs-lg) / 1.4 var(--font-ui)` (was `--type-body`, 22): the words being typed read a size up, level with the Composer. The field keeps its 48px height. A theme that set `--field-font` is unaffected.
|
|
15
|
+
|
|
16
|
+
### Field: no browser focus ring; the field's border goes full ink while the cursor is in it (Lh 2026-09-22)
|
|
17
|
+
The inner input drew the browser's own ring as a second box inside the field. Gone. Knob `--field-active-border` (default `currentColor`); coloured palettes keep the 25% border at rest only.
|
|
18
|
+
|
|
19
|
+
### Pin: the waiting cell shows a caret inside the pill, no focus ring (Lh 2026-09-22)
|
|
20
|
+
The `:focus-visible` outline sat outside the pill and was cropped by the sheet's edge. Gone. The cell the cursor waits in now has a full-ink border and a blinking bar drawn inside it (the native caret is hidden). Knobs `--pin-cell-active-border`, `--pin-caret-w`, `--pin-caret-h`.
|
|
21
|
+
|
|
5
22
|
## 0.5.0 — 2026-09-22
|
|
6
23
|
|
|
7
24
|
### Words are full ink; muted means inactive (Lh 2026-09-22)
|
package/css/base.css
CHANGED
|
@@ -118,9 +118,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
118
118
|
.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
119
|
/* an illustration among the words (Lh 2026-09-22): capped, so a tall picture never pushes the words under the bar;
|
|
120
120
|
the "where am I" dots under the words, centred — the same dots the centred card has under it */
|
|
121
|
-
.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; } /* height auto: an <img> with width/height attributes keeps its ratio inside the cap — no band under the picture */
|
|
121
|
+
.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
122
|
.base-content > .ph { width: 100%; height: var(--base-content-media-h, 14rem); }
|
|
123
123
|
.base-content > .pager__at { justify-self: center; }
|
|
124
|
+
/* the dots at the FOOT of the block (Lh 2026-09-22): a block that ends on .pager__at becomes a flex column so the dots
|
|
125
|
+
ride its bottom edge — the words keep the top, the free room opens between. The block must fill its cell for that:
|
|
126
|
+
in .pages it is stretched to the cell; a lone .step > .base-content grows for it (step.css). */
|
|
127
|
+
.base-content:has(> .pager__at:last-child) { display: flex; flex-direction: column; }
|
|
128
|
+
.base-content > .pager__at:last-child { margin-top: auto; align-self: center; }
|
|
124
129
|
.base-content .cta-pack {
|
|
125
130
|
display: flex;
|
|
126
131
|
align-items: start; /* buttons keep their own height, never the row's */
|
package/css/choice.css
CHANGED
|
@@ -204,10 +204,23 @@
|
|
|
204
204
|
text-align: center;
|
|
205
205
|
font: var(--pin-cell-font, var(--type-stat));
|
|
206
206
|
font-variant-numeric: tabular-nums;
|
|
207
|
-
caret-color: currentColor;
|
|
208
207
|
}
|
|
209
208
|
.pin__cell:not(:placeholder-shown) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
210
|
-
|
|
209
|
+
/* the cell the cursor waits in (Lh 2026-09-22): NO outline ring — it sat outside the pill and got cropped by the sheet's
|
|
210
|
+
edge. The sign is INSIDE the pill: a full-ink border and a blinking bar drawn where the digit will land (the native caret
|
|
211
|
+
is hidden — its height and place are the browser's; the pill's bar is one rule). A filled cell selects its digit instead. */
|
|
212
|
+
.pin__cell { caret-color: transparent; }
|
|
213
|
+
.pin__cell:focus { outline: none; border-color: var(--pin-cell-active-border, currentColor); }
|
|
214
|
+
.pin__cell:focus:placeholder-shown {
|
|
215
|
+
background-image: linear-gradient(currentColor, currentColor);
|
|
216
|
+
background-size: var(--pin-caret-w, 2px) var(--pin-caret-h, 1em);
|
|
217
|
+
background-position: center;
|
|
218
|
+
background-repeat: no-repeat;
|
|
219
|
+
}
|
|
220
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
221
|
+
.pin__cell:focus:placeholder-shown { animation: pin-caret 1.1s steps(1) infinite; }
|
|
222
|
+
@keyframes pin-caret { 50% { background-size: 0 0; } }
|
|
223
|
+
}
|
|
211
224
|
|
|
212
225
|
/* ---- composer: big text area with a bottom bar (icon · hint · send) ----
|
|
213
226
|
<div class="composer"><textarea class="composer__input" placeholder="What do you need?"></textarea>
|
package/css/forms.css
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
background: var(--field-bg, var(--action-bg));
|
|
14
14
|
color: var(--field-ink, var(--action-ink));
|
|
15
15
|
padding-inline: var(--field-pad-x, var(--sp-4));
|
|
16
|
-
min-height: var(--field-min-h, var(--tap));
|
|
16
|
+
min-height: var(--field-min-h, var(--tap-lg)); /* 56, the action field's height, for every field (Lh 2026-09-22: "ใหญ่ดี") */
|
|
17
17
|
min-width: 0;
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -22,19 +22,22 @@
|
|
|
22
22
|
border: 0;
|
|
23
23
|
background: transparent;
|
|
24
24
|
color: inherit;
|
|
25
|
-
font: var(--field-font, var(--
|
|
25
|
+
font: var(--field-font, 400 var(--fs-lg) / 1.4 var(--font-ui)); /* 26: the words being typed read a size up from body (Lh 2026-09-22); the Composer already does */
|
|
26
26
|
width: 100%;
|
|
27
27
|
min-width: 0;
|
|
28
|
-
outline
|
|
28
|
+
outline: none; /* the browser's ring drew a second box inside the field (Lh 2026-09-22) — the field itself tells focus */
|
|
29
|
+
caret-color: currentColor;
|
|
29
30
|
}
|
|
30
31
|
.field input::placeholder { color: inherit; opacity: 0.55; }
|
|
32
|
+
/* the field the cursor is in: its own border goes full ink, in every palette (the coloured ones fade it to 25% at rest) */
|
|
33
|
+
.field:has(:focus) { border-color: var(--field-active-border, currentColor); }
|
|
31
34
|
|
|
32
35
|
/* field with its action inside: date pick, send, go — the button sits flush right */
|
|
33
36
|
.field--action { padding-right: var(--sp-1); min-height: var(--field-action-min-h, var(--tap-lg)); }
|
|
34
37
|
.field--action > .icon-btn { margin-left: auto; }
|
|
35
38
|
|
|
36
39
|
/* colored palettes drop the visible outline elsewhere — fields keep one */
|
|
37
|
-
[data-palette] .field { border-color: color-mix(in srgb, currentColor 25%, transparent); }
|
|
40
|
+
[data-palette] .field:not(:has(:focus)) { border-color: color-mix(in srgb, currentColor 25%, transparent); }
|
|
38
41
|
|
|
39
42
|
/* ---- add-row: inline "+ add another" action ---- */
|
|
40
43
|
.add-row {
|
package/css/step.css
CHANGED
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
.step > :is(.base-content, .pages) {
|
|
90
90
|
flex: 0 1 auto;
|
|
91
91
|
}
|
|
92
|
+
.step:has(> .base-content > .pager__at:last-child, > .pages > .base-content > .pager__at:last-child) > :is(.base-content, .pages) { flex: 1 1 auto; } /* dots at the foot: the block takes the column (base.css) */
|
|
92
93
|
.step > :is(.base-content, .pages) {
|
|
93
94
|
min-height: 0;
|
|
94
95
|
margin: var(--_lead, 0) var(--screen-pad) 0; /* the screen's gap, moved in here from above the step; the inset, taken back from the bleed */
|
package/dist/cardds.css
CHANGED
|
@@ -494,9 +494,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
494
494
|
.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) */
|
|
495
495
|
/* an illustration among the words (Lh 2026-09-22): capped, so a tall picture never pushes the words under the bar;
|
|
496
496
|
the "where am I" dots under the words, centred — the same dots the centred card has under it */
|
|
497
|
-
.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; } /* height auto: an <img> with width/height attributes keeps its ratio inside the cap — no band under the picture */
|
|
497
|
+
.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 */
|
|
498
498
|
.base-content > .ph { width: 100%; height: var(--base-content-media-h, 14rem); }
|
|
499
499
|
.base-content > .pager__at { justify-self: center; }
|
|
500
|
+
/* the dots at the FOOT of the block (Lh 2026-09-22): a block that ends on .pager__at becomes a flex column so the dots
|
|
501
|
+
ride its bottom edge — the words keep the top, the free room opens between. The block must fill its cell for that:
|
|
502
|
+
in .pages it is stretched to the cell; a lone .step > .base-content grows for it (step.css). */
|
|
503
|
+
.base-content:has(> .pager__at:last-child) { display: flex; flex-direction: column; }
|
|
504
|
+
.base-content > .pager__at:last-child { margin-top: auto; align-self: center; }
|
|
500
505
|
.base-content .cta-pack {
|
|
501
506
|
display: flex;
|
|
502
507
|
align-items: start; /* buttons keep their own height, never the row's */
|
|
@@ -1538,6 +1543,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1538
1543
|
.step > :is(.base-content, .pages) {
|
|
1539
1544
|
flex: 0 1 auto;
|
|
1540
1545
|
}
|
|
1546
|
+
.step:has(> .base-content > .pager__at:last-child, > .pages > .base-content > .pager__at:last-child) > :is(.base-content, .pages) { flex: 1 1 auto; } /* dots at the foot: the block takes the column (base.css) */
|
|
1541
1547
|
.step > :is(.base-content, .pages) {
|
|
1542
1548
|
min-height: 0;
|
|
1543
1549
|
margin: var(--_lead, 0) var(--screen-pad) 0; /* the screen's gap, moved in here from above the step; the inset, taken back from the bleed */
|
|
@@ -1561,7 +1567,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1561
1567
|
background: var(--field-bg, var(--action-bg));
|
|
1562
1568
|
color: var(--field-ink, var(--action-ink));
|
|
1563
1569
|
padding-inline: var(--field-pad-x, var(--sp-4));
|
|
1564
|
-
min-height: var(--field-min-h, var(--tap));
|
|
1570
|
+
min-height: var(--field-min-h, var(--tap-lg)); /* 56, the action field's height, for every field (Lh 2026-09-22: "ใหญ่ดี") */
|
|
1565
1571
|
min-width: 0;
|
|
1566
1572
|
}
|
|
1567
1573
|
|
|
@@ -1570,19 +1576,22 @@ button { font: inherit; cursor: pointer; }
|
|
|
1570
1576
|
border: 0;
|
|
1571
1577
|
background: transparent;
|
|
1572
1578
|
color: inherit;
|
|
1573
|
-
font: var(--field-font, var(--
|
|
1579
|
+
font: var(--field-font, 400 var(--fs-lg) / 1.4 var(--font-ui)); /* 26: the words being typed read a size up from body (Lh 2026-09-22); the Composer already does */
|
|
1574
1580
|
width: 100%;
|
|
1575
1581
|
min-width: 0;
|
|
1576
|
-
outline
|
|
1582
|
+
outline: none; /* the browser's ring drew a second box inside the field (Lh 2026-09-22) — the field itself tells focus */
|
|
1583
|
+
caret-color: currentColor;
|
|
1577
1584
|
}
|
|
1578
1585
|
.field input::placeholder { color: inherit; opacity: 0.55; }
|
|
1586
|
+
/* the field the cursor is in: its own border goes full ink, in every palette (the coloured ones fade it to 25% at rest) */
|
|
1587
|
+
.field:has(:focus) { border-color: var(--field-active-border, currentColor); }
|
|
1579
1588
|
|
|
1580
1589
|
/* field with its action inside: date pick, send, go — the button sits flush right */
|
|
1581
1590
|
.field--action { padding-right: var(--sp-1); min-height: var(--field-action-min-h, var(--tap-lg)); }
|
|
1582
1591
|
.field--action > .icon-btn { margin-left: auto; }
|
|
1583
1592
|
|
|
1584
1593
|
/* colored palettes drop the visible outline elsewhere — fields keep one */
|
|
1585
|
-
[data-palette] .field { border-color: color-mix(in srgb, currentColor 25%, transparent); }
|
|
1594
|
+
[data-palette] .field:not(:has(:focus)) { border-color: color-mix(in srgb, currentColor 25%, transparent); }
|
|
1586
1595
|
|
|
1587
1596
|
/* ---- add-row: inline "+ add another" action ---- */
|
|
1588
1597
|
.add-row {
|
|
@@ -2531,10 +2540,23 @@ button { font: inherit; cursor: pointer; }
|
|
|
2531
2540
|
text-align: center;
|
|
2532
2541
|
font: var(--pin-cell-font, var(--type-stat));
|
|
2533
2542
|
font-variant-numeric: tabular-nums;
|
|
2534
|
-
caret-color: currentColor;
|
|
2535
2543
|
}
|
|
2536
2544
|
.pin__cell:not(:placeholder-shown) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
2537
|
-
|
|
2545
|
+
/* the cell the cursor waits in (Lh 2026-09-22): NO outline ring — it sat outside the pill and got cropped by the sheet's
|
|
2546
|
+
edge. The sign is INSIDE the pill: a full-ink border and a blinking bar drawn where the digit will land (the native caret
|
|
2547
|
+
is hidden — its height and place are the browser's; the pill's bar is one rule). A filled cell selects its digit instead. */
|
|
2548
|
+
.pin__cell { caret-color: transparent; }
|
|
2549
|
+
.pin__cell:focus { outline: none; border-color: var(--pin-cell-active-border, currentColor); }
|
|
2550
|
+
.pin__cell:focus:placeholder-shown {
|
|
2551
|
+
background-image: linear-gradient(currentColor, currentColor);
|
|
2552
|
+
background-size: var(--pin-caret-w, 2px) var(--pin-caret-h, 1em);
|
|
2553
|
+
background-position: center;
|
|
2554
|
+
background-repeat: no-repeat;
|
|
2555
|
+
}
|
|
2556
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2557
|
+
.pin__cell:focus:placeholder-shown { animation: pin-caret 1.1s steps(1) infinite; }
|
|
2558
|
+
@keyframes pin-caret { 50% { background-size: 0 0; } }
|
|
2559
|
+
}
|
|
2538
2560
|
|
|
2539
2561
|
/* ---- composer: big text area with a bottom bar (icon · hint · send) ----
|
|
2540
2562
|
<div class="composer"><textarea class="composer__input" placeholder="What do you need?"></textarea>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usableapp/cardds",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.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.",
|