@usableapp/cardds 0.7.67 → 0.7.68

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,16 @@
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.68 — 2026-10-07
6
+
7
+ ### Changed
8
+ - **A `Sticky` has an edge, and a short shadow** (Lh 2026-10-07) — a hairline edge runs round the note in its own shape (the folded corner, a `corner` notch), so a pale note still ends somewhere on a pale ground; the shadow is short and close under the note. Before: one wide soft shadow all round (8px of blur), which swallowed the edge of a pale note. Nothing to change in a screen. BREAKING for a theme: the knob `--sticky-drop` is gone — set `--sticky-edge-bg` (the edge's colour), `--sticky-shadow-bg` (the shadow's colour) and `--sticky-shadow-blur` instead.
9
+ - **In a chat, a note leans on its writer's own side** (Lh 2026-10-07) — in a `StickyThread` without `rail`, a note starts at the thread's edge under the avatar: theirs against the left, mine (`side="right"`) against the right. Before: notes started under the name, so both sides stood almost one over the other in the middle. A thread WITH `rail` is unchanged (the notes keep clear of the dotted line). Nothing to change in a screen.
10
+ - **A `Stamp` has fewer, larger teeth** (Lh 2026-10-07) — 12 across and 15 down; it was 20 and 25, which ran together into a fuzzy edge on a small stamp. Nothing to change in a screen.
11
+
12
+ ### Fixed
13
+ - **Safari / iPhone: a dragged `StampOn` left its shadow behind as a trail, and the note under it showed a hard grey band** — seen on the iOS Simulator. A draggable stamp is on its own layer now, and the note has no filter (see the edge and shadow above). Nothing to change in a screen.
14
+
5
15
  ## 0.7.67 — 2026-10-07
6
16
 
7
17
  ### Added
package/css/stamp.css CHANGED
@@ -31,8 +31,8 @@
31
31
  filter: drop-shadow(var(--stamp-drop, 0 var(--sp-1) var(--sp-2) rgb(0 0 0 / 0.16))); /* drop-shadow follows the teeth; a box-shadow would draw the rectangle */
32
32
  }
33
33
  .stamp__paper {
34
- --_pitch: 5cqw; /* one tooth: 20 across, 25 down — the holes land whole on every edge */
35
- --_hole: 1.6cqw;
34
+ --_pitch: calc(100cqw / 12); /* one tooth: 12 across, 15 down — the holes land whole on every edge. Fewer and larger than a real stamp's: a small stamp's teeth must still read (Lh 2026-10-07; it was 20 × 25) */
35
+ --_hole: 2.6cqw;
36
36
  position: absolute;
37
37
  inset: 0;
38
38
  background-color: var(--stamp-paper-bg, #fffdf8);
@@ -176,6 +176,7 @@
176
176
  pointer-events: auto; /* the layer it lies in lets a tap through to the surface; the stamp itself takes it */
177
177
  }
178
178
  .stamp-on > .stamp { max-width: none; }
179
+ .stamp-on--move { will-change: transform; } /* its own layer: Safari left the stamp's shadow behind as a trail along the drag when it was painted with the note (seen on the simulator, 2026-10-07) */
179
180
  .stamp-on--move { cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none; transition: scale var(--motion-press) ease-out; }
180
181
  .stamp-on--move:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
181
182
  .stamp-on.is-held { cursor: grabbing; z-index: 1; scale: 1.06; } /* lifted off the paper while the finger holds it */
package/css/sticky.css CHANGED
@@ -30,8 +30,27 @@
30
30
  min-height: var(--sticky-min-h, var(--_tall)); /* every note is one height, the height of a note being written (Lh 2026-10-07); more words still grow it */
31
31
  padding: 0 var(--_fold) var(--_fold) 0; /* = the line above, written whole */
32
32
  background-clip: content-box;
33
- filter: drop-shadow(var(--sticky-drop, 0 var(--sp-1) var(--sp-2) rgb(0 0 0 / 0.16))); /* drop-shadow follows the cut; a box-shadow would draw the square */
33
+ isolation: isolate; /* the two layers below stay under this note, over whatever is behind it */
34
34
  }
35
+ /* the edge and the shadow: two layers UNDER the paper, each drawn in the note's own shape by a gradient — never a filter on the
36
+ note (Safari repainted a filtered note as a hard grey band once a stamp on it had moved; Lh 2026-10-07).
37
+ ::after is the EDGE: the shape one hairline larger all round, so a pale note still ends somewhere on a pale ground.
38
+ ::before is the SHADOW: the shape set a little down and in, softened on its own layer — short and close, paper lying on a table. */
39
+ .sticky::before, .sticky::after {
40
+ content: "";
41
+ position: absolute;
42
+ z-index: -1;
43
+ pointer-events: none;
44
+ border-radius: var(--sticky-radius, var(--sp-1));
45
+ background-image: linear-gradient(-45deg, transparent calc(var(--_fold) * 0.7071), var(--_ink) 0); /* the folded corner, cut */
46
+ }
47
+ .sticky::after { --_ink: var(--sticky-edge-bg, rgb(0 0 0 / 0.14)); inset: calc(-1 * var(--border-w)); }
48
+ .sticky::before {
49
+ --_ink: var(--sticky-shadow-bg, rgb(0 0 0 / 0.28));
50
+ inset: var(--sp-1) var(--sp-0) calc(-1 * var(--sp-0)) var(--sp-0);
51
+ filter: blur(var(--sticky-shadow-blur, var(--sp-0)));
52
+ }
53
+ .sticky--flat::before, .sticky--flat::after { background: var(--_ink); }
35
54
  /* the tones — the names in src/sticky/tones.ts. A swatch of the chooser wears the same class. */
36
55
  .sticky--cotton { background-color: var(--sticky-cotton-bg, #f3efe2); }
37
56
  .sticky--blush { background-color: var(--sticky-blush-bg, #f6d5da); }
@@ -92,8 +111,8 @@
92
111
  /* --square: the note to write on — 1:1, as wide as its column up to a cap, in the middle */
93
112
  .sticky--square { width: 100%; max-width: var(--sticky-square-max-w, var(--_cap)); aspect-ratio: 1; margin-inline: auto; }
94
113
 
95
- /* --corner: a control stands ON the bottom-right corner, half outside. The paper is notched round it (a mask, so the
96
- shadow follows the notch and no ground colour is needed); the note has no fold. */
114
+ /* --corner: a control stands ON the bottom-right corner, half outside. The paper is notched round it (a mask, so no
115
+ ground colour is needed; the edge and the shadow are notched the same way); the note has no fold. */
97
116
  .sticky__corner {
98
117
  position: absolute;
99
118
  right: 0;
@@ -110,6 +129,7 @@
110
129
  max-width: calc(var(--sticky-max-w, 100%) - var(--_corner) / 2);
111
130
  margin: 0 calc(var(--_corner) / 2) calc(var(--_corner) / 2) 0;
112
131
  }
132
+ .sticky--corner::before, .sticky--corner::after { background-image: radial-gradient(circle at 100% 100%, transparent calc(var(--_corner) / 2 + var(--sp-1)), var(--_ink) 0); }
113
133
  .sticky--corner > .sticky__paper {
114
134
  --_notch: calc(var(--_corner) / 2 + var(--sp-1));
115
135
  clip-path: none;
@@ -175,7 +195,7 @@
175
195
  </div>
176
196
  <div class="sticky-post sticky-post--right">…</div>
177
197
  </div>
178
- Two columns: the avatar, then everything else — so the notes start under the NAME. --right swaps the columns. */
198
+ Two columns: the avatar, then everything else. --right swaps the columns. */
179
199
  .sticky-thread { display: grid; gap: var(--sticky-thread-gap, var(--sp-6)); --_thread-gap: var(--sticky-thread-gap, var(--sp-6)); } /* --_thread-gap: the same value, for the rail to run through */
180
200
  .sticky-post {
181
201
  display: grid;
@@ -193,6 +213,8 @@
193
213
  .sticky-post__notes > .sticky { max-width: var(--sticky-post-max-w, 85%); } /* a note leaves the far side open, as a bubble does */
194
214
  .sticky-post__rail { display: none; grid-area: 2 / 1; justify-self: center; align-self: stretch; color: var(--sticky-thread-rail-ink, var(--on-surface-muted)); border-inline-start: var(--rule-w) dotted currentColor; margin-block-end: calc(-1 * var(--_thread-gap)); } /* runs on through the gap to the next avatar */
195
215
  .sticky-thread--rail > .sticky-post:not(:last-child) > .sticky-post__rail { display: block; }
216
+ /* a chat (no rail): a note leans on its writer's own edge of the thread — it starts under the avatar, not under the name, so theirs and mine stand apart (Lh 2026-10-07: both sat in the middle). With a rail the notes keep clear of it, under the name. */
217
+ .sticky-thread:not(.sticky-thread--rail) > .sticky-post > .sticky-post__notes { grid-column: 1 / -1; }
196
218
  .sticky-post--right { grid-template-columns: minmax(0, 1fr) auto; }
197
219
  .sticky-post--right > .sticky-post__who { grid-column: 2; }
198
220
  .sticky-post--right > .sticky-post__head { grid-column: 1; flex-direction: row-reverse; }
package/dist/cardds.css CHANGED
@@ -3659,8 +3659,27 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3659
3659
  min-height: var(--sticky-min-h, var(--_tall)); /* every note is one height, the height of a note being written (Lh 2026-10-07); more words still grow it */
3660
3660
  padding: 0 var(--_fold) var(--_fold) 0; /* = the line above, written whole */
3661
3661
  background-clip: content-box;
3662
- filter: drop-shadow(var(--sticky-drop, 0 var(--sp-1) var(--sp-2) rgb(0 0 0 / 0.16))); /* drop-shadow follows the cut; a box-shadow would draw the square */
3662
+ isolation: isolate; /* the two layers below stay under this note, over whatever is behind it */
3663
3663
  }
3664
+ /* the edge and the shadow: two layers UNDER the paper, each drawn in the note's own shape by a gradient — never a filter on the
3665
+ note (Safari repainted a filtered note as a hard grey band once a stamp on it had moved; Lh 2026-10-07).
3666
+ ::after is the EDGE: the shape one hairline larger all round, so a pale note still ends somewhere on a pale ground.
3667
+ ::before is the SHADOW: the shape set a little down and in, softened on its own layer — short and close, paper lying on a table. */
3668
+ .sticky::before, .sticky::after {
3669
+ content: "";
3670
+ position: absolute;
3671
+ z-index: -1;
3672
+ pointer-events: none;
3673
+ border-radius: var(--sticky-radius, var(--sp-1));
3674
+ background-image: linear-gradient(-45deg, transparent calc(var(--_fold) * 0.7071), var(--_ink) 0); /* the folded corner, cut */
3675
+ }
3676
+ .sticky::after { --_ink: var(--sticky-edge-bg, rgb(0 0 0 / 0.14)); inset: calc(-1 * var(--border-w)); }
3677
+ .sticky::before {
3678
+ --_ink: var(--sticky-shadow-bg, rgb(0 0 0 / 0.28));
3679
+ inset: var(--sp-1) var(--sp-0) calc(-1 * var(--sp-0)) var(--sp-0);
3680
+ filter: blur(var(--sticky-shadow-blur, var(--sp-0)));
3681
+ }
3682
+ .sticky--flat::before, .sticky--flat::after { background: var(--_ink); }
3664
3683
  /* the tones — the names in src/sticky/tones.ts. A swatch of the chooser wears the same class. */
3665
3684
  .sticky--cotton { background-color: var(--sticky-cotton-bg, #f3efe2); }
3666
3685
  .sticky--blush { background-color: var(--sticky-blush-bg, #f6d5da); }
@@ -3721,8 +3740,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3721
3740
  /* --square: the note to write on — 1:1, as wide as its column up to a cap, in the middle */
3722
3741
  .sticky--square { width: 100%; max-width: var(--sticky-square-max-w, var(--_cap)); aspect-ratio: 1; margin-inline: auto; }
3723
3742
 
3724
- /* --corner: a control stands ON the bottom-right corner, half outside. The paper is notched round it (a mask, so the
3725
- shadow follows the notch and no ground colour is needed); the note has no fold. */
3743
+ /* --corner: a control stands ON the bottom-right corner, half outside. The paper is notched round it (a mask, so no
3744
+ ground colour is needed; the edge and the shadow are notched the same way); the note has no fold. */
3726
3745
  .sticky__corner {
3727
3746
  position: absolute;
3728
3747
  right: 0;
@@ -3739,6 +3758,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3739
3758
  max-width: calc(var(--sticky-max-w, 100%) - var(--_corner) / 2);
3740
3759
  margin: 0 calc(var(--_corner) / 2) calc(var(--_corner) / 2) 0;
3741
3760
  }
3761
+ .sticky--corner::before, .sticky--corner::after { background-image: radial-gradient(circle at 100% 100%, transparent calc(var(--_corner) / 2 + var(--sp-1)), var(--_ink) 0); }
3742
3762
  .sticky--corner > .sticky__paper {
3743
3763
  --_notch: calc(var(--_corner) / 2 + var(--sp-1));
3744
3764
  clip-path: none;
@@ -3804,7 +3824,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3804
3824
  </div>
3805
3825
  <div class="sticky-post sticky-post--right">…</div>
3806
3826
  </div>
3807
- Two columns: the avatar, then everything else — so the notes start under the NAME. --right swaps the columns. */
3827
+ Two columns: the avatar, then everything else. --right swaps the columns. */
3808
3828
  .sticky-thread { display: grid; gap: var(--sticky-thread-gap, var(--sp-6)); --_thread-gap: var(--sticky-thread-gap, var(--sp-6)); } /* --_thread-gap: the same value, for the rail to run through */
3809
3829
  .sticky-post {
3810
3830
  display: grid;
@@ -3822,6 +3842,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3822
3842
  .sticky-post__notes > .sticky { max-width: var(--sticky-post-max-w, 85%); } /* a note leaves the far side open, as a bubble does */
3823
3843
  .sticky-post__rail { display: none; grid-area: 2 / 1; justify-self: center; align-self: stretch; color: var(--sticky-thread-rail-ink, var(--on-surface-muted)); border-inline-start: var(--rule-w) dotted currentColor; margin-block-end: calc(-1 * var(--_thread-gap)); } /* runs on through the gap to the next avatar */
3824
3844
  .sticky-thread--rail > .sticky-post:not(:last-child) > .sticky-post__rail { display: block; }
3845
+ /* a chat (no rail): a note leans on its writer's own edge of the thread — it starts under the avatar, not under the name, so theirs and mine stand apart (Lh 2026-10-07: both sat in the middle). With a rail the notes keep clear of it, under the name. */
3846
+ .sticky-thread:not(.sticky-thread--rail) > .sticky-post > .sticky-post__notes { grid-column: 1 / -1; }
3825
3847
  .sticky-post--right { grid-template-columns: minmax(0, 1fr) auto; }
3826
3848
  .sticky-post--right > .sticky-post__who { grid-column: 2; }
3827
3849
  .sticky-post--right > .sticky-post__head { grid-column: 1; flex-direction: row-reverse; }
@@ -3863,8 +3885,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
3863
3885
  filter: drop-shadow(var(--stamp-drop, 0 var(--sp-1) var(--sp-2) rgb(0 0 0 / 0.16))); /* drop-shadow follows the teeth; a box-shadow would draw the rectangle */
3864
3886
  }
3865
3887
  .stamp__paper {
3866
- --_pitch: 5cqw; /* one tooth: 20 across, 25 down — the holes land whole on every edge */
3867
- --_hole: 1.6cqw;
3888
+ --_pitch: calc(100cqw / 12); /* one tooth: 12 across, 15 down — the holes land whole on every edge. Fewer and larger than a real stamp's: a small stamp's teeth must still read (Lh 2026-10-07; it was 20 × 25) */
3889
+ --_hole: 2.6cqw;
3868
3890
  position: absolute;
3869
3891
  inset: 0;
3870
3892
  background-color: var(--stamp-paper-bg, #fffdf8);
@@ -4008,6 +4030,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4008
4030
  pointer-events: auto; /* the layer it lies in lets a tap through to the surface; the stamp itself takes it */
4009
4031
  }
4010
4032
  .stamp-on > .stamp { max-width: none; }
4033
+ .stamp-on--move { will-change: transform; } /* its own layer: Safari left the stamp's shadow behind as a trail along the drag when it was painted with the note (seen on the simulator, 2026-10-07) */
4011
4034
  .stamp-on--move { cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none; transition: scale var(--motion-press) ease-out; }
4012
4035
  .stamp-on--move:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
4013
4036
  .stamp-on.is-held { cursor: grabbing; z-index: 1; scale: 1.06; } /* lifted off the paper while the finger holds it */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usableapp/cardds",
3
- "version": "0.7.67",
3
+ "version": "0.7.68",
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.",