@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 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
- .pin__cell:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
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(--type-body));
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-offset: var(--sp-1);
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(--type-body));
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-offset: var(--sp-1);
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
- .pin__cell:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
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.0",
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.",