@usableapp/cardds 0.7.103 → 0.7.104
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 +27 -0
- package/css/actions.css +102 -36
- package/css/base.css +139 -15
- package/css/card.css +308 -79
- package/css/choice.css +25 -33
- package/css/finish.css +28 -27
- package/css/forms.css +6 -14
- package/css/journey.css +4 -4
- package/css/layover.css +26 -24
- package/css/lists.css +8 -6
- package/css/media.css +13 -74
- package/css/numbers.css +37 -32
- package/css/palette.css +1 -1
- package/css/people.css +3 -8
- package/css/post.css +23 -19
- package/css/sheet.css +84 -56
- package/css/stack.css +39 -215
- package/css/stamp.css +41 -29
- package/css/step.css +1 -107
- package/css/sticky.css +43 -41
- package/css/theme.template.css +12 -0
- package/css/tokens.css +100 -26
- package/dist/actions/Chip.d.ts +1 -1
- package/dist/actions/Chip.js +3 -2
- package/dist/cardds.css +1053 -868
- package/dist/cards/Card.d.ts +3 -3
- package/dist/cards/Card.js +1 -1
- package/dist/cards/CardFeed.js +7 -9
- package/dist/catalog.json +1 -1
- package/dist/layover/Snackbar.js +2 -2
- package/dist/lists/Bubble.d.ts +1 -1
- package/dist/lists/Bubble.js +1 -1
- package/dist/lists/Row.js +5 -7
- package/dist/numbers/Picker.js +11 -18
- package/dist/people/Avatar.d.ts +1 -1
- package/dist/people/Avatar.js +3 -2
- package/dist/post/PostDesk.js +6 -9
- package/dist/post/PostLetter.js +4 -4
- package/dist/pressable.d.ts +9 -0
- package/dist/pressable.js +6 -0
- package/dist/room.d.ts +5 -0
- package/dist/room.js +9 -0
- package/dist/scaffold/Panes.js +8 -14
- package/dist/sheets/Sheet.d.ts +1 -1
- package/dist/sheets/Sheet.js +2 -3
- package/dist/sheets/SheetSteps.js +3 -3
- package/dist/sheets/StackSheet.d.ts +1 -1
- package/dist/sheets/sheetStepsPile.js +2 -3
- package/dist/sheets/stackCard.js +67 -115
- package/dist/state.d.ts +21 -0
- package/dist/state.js +28 -1
- package/dist/sticky/Sticky.js +3 -7
- package/dist/templates/Home.d.ts +1 -1
- package/dist/templates/Splash.js +2 -2
- package/dist/templates/Walkthrough.js +4 -5
- package/docs/reference.md +71 -9
- package/lib/catalog.mjs +23 -7
- package/lint/rules.mjs +7 -5
- package/lint/values.mjs +144 -0
- package/package.json +6 -4
package/css/media.css
CHANGED
|
@@ -1,52 +1,16 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
cardds/media.css — media & display inside cards:
|
|
3
|
-
|
|
3
|
+
quote · tile grid · waveform (the cover card and the fold are card variants: card.css)
|
|
4
4
|
============================================================ */
|
|
5
5
|
|
|
6
|
-
/* ---- cover: the image IS the card; text over a scrim ----
|
|
7
|
-
<article class="card card--cover"><div class="ph" role="img"></div>
|
|
8
|
-
<div class="card__head"><button class="icon-btn icon-btn--sm">♥</button></div>
|
|
9
|
-
<div><h2 class="t-h1">Art Institute</h2><p class="t-body">111 South Michigan Ave</p></div>
|
|
10
|
-
<div class="row">…meta…</div></article> */
|
|
11
|
-
.card--cover {
|
|
12
|
-
--card-bg: var(--card-3-bg);
|
|
13
|
-
--dark: var(--card-3-dark);
|
|
14
|
-
--accent: var(--card-ink);
|
|
15
|
-
--accent-dark: calc(1 - var(--dark));
|
|
16
|
-
min-height: var(--_cover-min-h);
|
|
17
|
-
align-content: space-between;
|
|
18
|
-
overflow: hidden;
|
|
19
|
-
isolation: isolate;
|
|
20
|
-
}
|
|
21
|
-
.card--cover > img,
|
|
22
|
-
.card--cover > .ph {
|
|
23
|
-
position: absolute;
|
|
24
|
-
inset: 0;
|
|
25
|
-
width: 100%;
|
|
26
|
-
height: 100%;
|
|
27
|
-
object-fit: cover;
|
|
28
|
-
z-index: -2;
|
|
29
|
-
border: 0;
|
|
30
|
-
border-radius: 0;
|
|
31
|
-
min-height: 0;
|
|
32
|
-
}
|
|
33
|
-
/* scrim: dark at both ends so the meta stays readable */
|
|
34
|
-
.card--cover::before {
|
|
35
|
-
content: "";
|
|
36
|
-
position: absolute;
|
|
37
|
-
inset: 0;
|
|
38
|
-
z-index: -1;
|
|
39
|
-
background: linear-gradient(to bottom, var(--scrim), transparent 40% 60%, var(--scrim));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
6
|
/* ---- quote: giant mark, big text, attribution ----
|
|
43
7
|
<blockquote class="quote"><p class="quote__text">It's overcast…</p><footer class="quote__by">Wednesday 15</footer></blockquote> */
|
|
44
8
|
.quote { margin: 0; display: grid; gap: var(--quote-gap, var(--sp-3)); }
|
|
45
9
|
.quote::before {
|
|
46
10
|
content: "\201C";
|
|
47
|
-
font:
|
|
11
|
+
font: var(--fw-black) calc(var(--fs-h1) * 3) / var(--lh-mark) var(--font-display);
|
|
48
12
|
height: var(--sp-10);
|
|
49
|
-
opacity:
|
|
13
|
+
opacity: var(--fade-most);
|
|
50
14
|
}
|
|
51
15
|
.quote__text { margin: 0; font: var(--quote-text-font, var(--type-h1)); letter-spacing: var(--track-h1); text-wrap: balance; }
|
|
52
16
|
.quote--sm .quote__text { font: var(--type-h2); letter-spacing: var(--track-h2); }
|
|
@@ -60,7 +24,7 @@
|
|
|
60
24
|
grid-template-columns: repeat(auto-fill, minmax(min(var(--tile-min), 100%), 1fr));
|
|
61
25
|
gap: var(--tile-grid-gap, var(--sp-3));
|
|
62
26
|
}
|
|
63
|
-
.tile-grid--3 { --tile-min:
|
|
27
|
+
.tile-grid--3 { --tile-min: calc(var(--tap-base) * 2); } /* three across on a phone (attachments, upload kinds) */
|
|
64
28
|
.tile {
|
|
65
29
|
display: grid;
|
|
66
30
|
align-content: space-between;
|
|
@@ -107,42 +71,18 @@
|
|
|
107
71
|
.tile--5 { background: var(--tile-5-bg, var(--card-5-bg)); --dark: var(--card-5-dark); color: var(--tile-5-ink, var(--card-ink)); border-color: transparent; }
|
|
108
72
|
.tile--3 { background: var(--tile-3-bg, var(--card-3-bg)); --dark: var(--card-3-dark); color: var(--tile-3-ink, var(--card-ink)); border-color: transparent; }
|
|
109
73
|
|
|
110
|
-
/* ---- fold: a sticky-note corner on any card (decorative) ----
|
|
111
|
-
No border. The card's outline follows its border box, so it ran straight
|
|
112
|
-
across the squared bottom-right corner and cut through the fold's diagonal —
|
|
113
|
-
the corner read as a notch with a line through it rather than a lifted flap.
|
|
114
|
-
The COLOUR goes transparent rather than the border going to 0, so the box
|
|
115
|
-
keeps its geometry and a fold card still measures the same as the cards
|
|
116
|
-
beside it. */
|
|
117
|
-
.card--fold {
|
|
118
|
-
border-color: transparent;
|
|
119
|
-
border-bottom-right-radius: 0;
|
|
120
|
-
background-image: linear-gradient(to top left,
|
|
121
|
-
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 */
|
|
122
|
-
color-mix(in srgb, currentColor 18%, var(--card-bg)) 50% 100%);
|
|
123
|
-
background-repeat: no-repeat;
|
|
124
|
-
background-position: bottom right;
|
|
125
|
-
background-size: var(--sp-8) var(--sp-8);
|
|
126
|
-
background-origin: border-box; /* the flap sits on the true corner, not inside the (transparent) border */
|
|
127
|
-
/* the corner is CUT: the triangle under the flap shows what is behind the card. Painted in the card's own colour it
|
|
128
|
-
read as the square corner of a second card underneath (Lh 2026-09-23). The polygon reaches 3rem past the other
|
|
129
|
-
edges so a coloured palette's shadow is not clipped there. */
|
|
130
|
-
clip-path: polygon(-3rem -3rem, calc(100% + 3rem) -3rem, calc(100% + 3rem) calc(100% - var(--sp-8) - 3rem),
|
|
131
|
-
calc(100% - var(--sp-8) - 3rem) calc(100% + 3rem), -3rem calc(100% + 3rem));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
74
|
/* ---- wave: audio waveform stand-in for voice rows ----
|
|
135
75
|
<span class="wave"></span> — sits in a .row__body, fills the width */
|
|
136
76
|
.wave {
|
|
137
77
|
/* only WHOLE bars show (Lh 2026-09-23 — a clipped last bar read thinner): the columns are as many whole bar-widths as fit
|
|
138
78
|
(auto-fill), spread edge to edge; bars that do not fit go to rows of no height, clipped away */
|
|
139
79
|
display: grid;
|
|
140
|
-
grid-template-columns: repeat(auto-fit, var(--wave-bar-width,
|
|
80
|
+
grid-template-columns: repeat(auto-fit, var(--wave-bar-width, var(--hairline-w))); /* auto-FIT: columns no bar fills collapse, so a wide row still spreads its bars edge to edge */
|
|
141
81
|
grid-template-rows: 100%;
|
|
142
82
|
grid-auto-rows: 0;
|
|
143
83
|
justify-content: space-between;
|
|
144
84
|
align-items: center;
|
|
145
|
-
column-gap: var(--wave-gap,
|
|
85
|
+
column-gap: var(--wave-gap, var(--hairline-w));
|
|
146
86
|
height: var(--wave-h, var(--icon));
|
|
147
87
|
width: 100%;
|
|
148
88
|
overflow: hidden;
|
|
@@ -168,7 +108,6 @@
|
|
|
168
108
|
}
|
|
169
109
|
.mosaic > :first-child { grid-row: span 2; }
|
|
170
110
|
.mosaic > * { min-height: 0; border-radius: 0; } /* opacity/border-width here were no-ops: .ph restates its own below, and nothing else in a mosaic carries either by default */
|
|
171
|
-
.mosaic > .ph { opacity: var(--off); border-width: var(--border-w); }
|
|
172
111
|
.mosaic > img { width: 100%; height: 100%; object-fit: cover; }
|
|
173
112
|
|
|
174
113
|
/* ---- postcard: a member's introduction in the shape of a cover card — the photo fills it,
|
|
@@ -226,8 +165,7 @@
|
|
|
226
165
|
.postcard__turn:focus-visible { outline-offset: calc(-1 * var(--sp-1)); } /* the ring inside the card's edge */
|
|
227
166
|
/* the front: the photo (or the wireframe box) fills it; the band reads on a scrim */
|
|
228
167
|
.postcard__front { background: var(--postcard-front-bg, var(--card-3-bg)); --dark: var(--card-3-dark); color: var(--postcard-front-ink, var(--card-ink)); }
|
|
229
|
-
.postcard__front .postcard__turn > img
|
|
230
|
-
.postcard__front .postcard__turn > .ph {
|
|
168
|
+
.postcard__front .postcard__turn > img {
|
|
231
169
|
position: absolute;
|
|
232
170
|
inset: 0;
|
|
233
171
|
width: 100%;
|
|
@@ -241,7 +179,7 @@
|
|
|
241
179
|
content: "";
|
|
242
180
|
position: absolute;
|
|
243
181
|
inset: 0;
|
|
244
|
-
z-index:
|
|
182
|
+
z-index: var(--z-over);
|
|
245
183
|
pointer-events: none;
|
|
246
184
|
background: linear-gradient(to top, var(--scrim), transparent 45%);
|
|
247
185
|
}
|
|
@@ -257,7 +195,7 @@
|
|
|
257
195
|
Taps between its words fall through to the turn; its buttons take their own. */
|
|
258
196
|
.postcard__band {
|
|
259
197
|
position: relative;
|
|
260
|
-
z-index: 2;
|
|
198
|
+
z-index: var(--z-over-2);
|
|
261
199
|
display: flex;
|
|
262
200
|
align-items: end;
|
|
263
201
|
gap: var(--postcard-band-gap, var(--sp-3));
|
|
@@ -267,9 +205,10 @@
|
|
|
267
205
|
.postcard__band > * { pointer-events: auto; }
|
|
268
206
|
.postcard__words { display: grid; gap: var(--postcard-words-gap, var(--sp-1)); flex: 1; min-width: 0; pointer-events: none; }
|
|
269
207
|
.postcard__front .postcard__band { --accent: var(--postcard-front-ink, var(--card-ink)); --accent-dark: calc(1 - var(--dark)); }
|
|
270
|
-
.postcard__band .icon-btn[aria-pressed="true"] .icon { fill: currentColor; }
|
|
271
208
|
/* a voice note on the back: no words to read — its timeline (a Progress), its length and the control to hear it
|
|
272
209
|
under them, at the middle over the turn, on the face's inset; the app plays and keeps the time */
|
|
273
|
-
.postcard__voice { position: absolute; inset: 0; z-index: 2; display: grid; align-content: center; justify-items: center; gap: var(--postcard-voice-gap, var(--sp-3)); padding: var(--postcard-face-pad, var(--card-pad-rest)); pointer-events: none; }
|
|
210
|
+
.postcard__voice { position: absolute; inset: 0; z-index: var(--z-over-2); display: grid; align-content: center; justify-items: center; gap: var(--postcard-voice-gap, var(--sp-3)); padding: var(--postcard-face-pad, var(--card-pad-rest)); pointer-events: none; }
|
|
274
211
|
.postcard__voice > * { pointer-events: auto; }
|
|
275
|
-
|
|
212
|
+
|
|
213
|
+
/* ==== the tile a choice draws, picked (choice.css drives it with its input) (#62: a block is written in its own file) ==== */
|
|
214
|
+
.tile--pick:has(:checked) { background: var(--accent); --dark: var(--accent-dark); color: var(--card-ink); border-color: transparent; }
|
package/css/numbers.css
CHANGED
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
border-radius: 50%;
|
|
29
29
|
background: conic-gradient(var(--accent) calc(var(--value) * 1%), var(--ink-tint) 0);
|
|
30
30
|
/* masks only read alpha: currentColor = "opaque" without naming a color */
|
|
31
|
-
mask: radial-gradient(farthest-side, transparent calc(100% - var(--_w) -
|
|
32
|
-
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - var(--_w) -
|
|
31
|
+
mask: radial-gradient(farthest-side, transparent calc(100% - var(--_w) - var(--px)), currentColor calc(100% - var(--_w)));
|
|
32
|
+
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - var(--_w) - var(--px)), currentColor calc(100% - var(--_w)));
|
|
33
33
|
}
|
|
34
34
|
.ring__value { font: var(--ring-value-font, var(--type-stat)); letter-spacing: var(--track-display); }
|
|
35
35
|
.ring__label { font: var(--ring-label-font, var(--type-caption)); color: var(--ring-label-ink, var(--card-muted)); }
|
|
36
36
|
.ring--sm { --_size: var(--avatar-xl); --_w: var(--sp-3); }
|
|
37
|
-
.ring--sm .ring__value { font:
|
|
37
|
+
.ring--sm .ring__value { font: var(--fw-heavy) var(--fs-2xl) / var(--lh-solid) var(--font-display); }
|
|
38
38
|
.ring--xs { --_size: var(--tap-sm); --_w: var(--dot-xs); } /* inline counter (step 3 of 5) */
|
|
39
39
|
.ring--xs .ring__value { font: var(--type-caption); }
|
|
40
40
|
|
|
@@ -75,44 +75,31 @@
|
|
|
75
75
|
}
|
|
76
76
|
.track__step:first-child::before { display: none; }
|
|
77
77
|
.track__step.is-done::before { background: var(--accent); }
|
|
78
|
-
.track__step > .icon
|
|
79
|
-
.track__step > .track__dot {
|
|
80
|
-
width: var(--_step);
|
|
81
|
-
height: var(--_step);
|
|
78
|
+
.track__step > .icon { --icon-size: var(--_step); --icon-pad: calc(var(--_step) * 7 / 32); --icon-radius: 50%; --icon-bg: var(--ink-rail); --icon-ink: var(--card-ink); }
|
|
79
|
+
.track__step > .track__dot { /* a number in the round (TrackStep num) */
|
|
82
80
|
flex: none;
|
|
83
|
-
padding: calc(var(--_step) * 7 / 32); /* the glyph = 18 in the 32 round */
|
|
84
81
|
border-radius: 50%;
|
|
85
|
-
background: var(--ink-
|
|
82
|
+
background: var(--ink-tint); /* a number is words: on the 30% rail it fell under 4.5:1 on a mid-dark track; the 12% tint is what every tone holds its ink on */
|
|
86
83
|
color: var(--card-ink);
|
|
87
|
-
}
|
|
88
|
-
.track__step > .track__dot { background: var(--ink-tint); } /* a number is words: on the 30% rail it fell under 4.5:1 on a mid-dark track; the 12% tint is what every tone holds its ink on */
|
|
89
|
-
.track__step > .track__dot { /* a number in the round (TrackStep num) */
|
|
90
84
|
display: grid; place-items: center; box-sizing: border-box;
|
|
91
|
-
font: var(--track-dot-font, var(--type-caption)); line-height:
|
|
85
|
+
font: var(--track-dot-font, var(--type-caption)); line-height: var(--lh-solid); font-variant-numeric: tabular-nums;
|
|
92
86
|
width: var(--track-dot-size, 1.877em); height: var(--track-dot-size, 1.877em); /* em, not rem (Lh 2026-10-03, as the SheetSteps' number and the StackSheet's count): the round is cut from ITS OWN
|
|
93
87
|
number's size — 1.877em of a 17px caption = the 32px of an icon's round; at a larger text it grows with its number, and the pill with it */
|
|
94
88
|
padding: 0 0 var(--track-dot-lift, 0.1em); /* the room UNDER the number lifts it onto the round's middle: a digit has no descender */
|
|
95
89
|
}
|
|
96
|
-
.track__step.is-done > .icon
|
|
90
|
+
.track__step.is-done > .icon { --icon-bg: var(--accent); --icon-ink: var(--on-accent); }
|
|
97
91
|
.track__step.is-done > .track__dot { background: var(--accent); color: var(--on-accent); }
|
|
98
92
|
|
|
99
93
|
/* ---- band: data card — 2-line label · giant number · footnote ----
|
|
100
94
|
<article class="card card--band"><span class="band__label">Hours<br>banked</span><span class="band__num">18</span><span class="band__foot">this week</span></article> */
|
|
101
|
-
.
|
|
102
|
-
grid-template-columns: 1fr auto;
|
|
103
|
-
grid-template-areas: "label num" "foot num";
|
|
104
|
-
align-items: end;
|
|
105
|
-
gap: var(--sp-2) var(--sp-4);
|
|
106
|
-
min-height: var(--card-band-min-h, var(--tile-h));
|
|
107
|
-
}
|
|
108
|
-
.band__label { grid-area: label; align-self: start; font: var(--band-label-font, var(--type-label)); font-size: var(--band-label-font-size, var(--fs-body)); line-height: 1.2; }
|
|
95
|
+
.band__label { grid-area: label; align-self: start; font: var(--band-label-font, var(--type-label)); font-size: var(--band-label-font-size, var(--fs-body)); line-height: var(--lh-label); }
|
|
109
96
|
.band__num {
|
|
110
97
|
grid-area: num;
|
|
111
|
-
font:
|
|
98
|
+
font: var(--fw-heavy) var(--fs-giant) / var(--lh-band) var(--font-display);
|
|
112
99
|
letter-spacing: var(--track-display);
|
|
113
100
|
font-variant-numeric: tabular-nums;
|
|
114
101
|
}
|
|
115
|
-
.band__num small { font-size: 0.5em; font-weight:
|
|
102
|
+
.band__num small { font-size: 0.5em; font-weight: var(--fw-bold); margin-right: 0.1em; } /* unit or % */
|
|
116
103
|
.band__foot { grid-area: foot; font: var(--band-foot-font, var(--type-caption)); color: var(--band-foot-ink, var(--card-muted)); }
|
|
117
104
|
/* band stack: ONE card boundary; bands are frameless inside it, a hairline
|
|
118
105
|
between each. Bands may still carry slot tints (card--2 / card--3). */
|
|
@@ -122,8 +109,6 @@
|
|
|
122
109
|
border-radius: var(--band-stack-radius, var(--r-card));
|
|
123
110
|
overflow: hidden;
|
|
124
111
|
}
|
|
125
|
-
.band-stack > .card--band { border: 0; border-radius: 0; box-shadow: none; }
|
|
126
|
-
.band-stack > .card--band + .card--band { border-top: var(--border-w) solid var(--hairline); }
|
|
127
112
|
|
|
128
113
|
/* ---- dotgrid: progress over time as dots — --total, --done, --cols ----
|
|
129
114
|
one element, no children:
|
|
@@ -157,7 +142,7 @@
|
|
|
157
142
|
);
|
|
158
143
|
}
|
|
159
144
|
/* no round(): show the grid without the done fill rather than a broken shape */
|
|
160
|
-
@supports not (width: round(up,
|
|
145
|
+
@supports not (width: round(up, 0.0625rem, 0.0625rem)) {
|
|
161
146
|
.dotgrid::before { display: none; }
|
|
162
147
|
}
|
|
163
148
|
/* ---- picker: horizontal snap picker, centre value big ----
|
|
@@ -202,7 +187,7 @@
|
|
|
202
187
|
.picker__unit { font: var(--picker-unit-font, var(--type-caption)); color: var(--picker-unit-ink, var(--card-muted)); }
|
|
203
188
|
/* the scrolled-to item lights up by position — no JS */
|
|
204
189
|
@supports (animation-timeline: view(x)) {
|
|
205
|
-
.picker__item { animation: picker-focus linear both; animation-timeline: view(x); }
|
|
190
|
+
.picker__item { animation: picker-focus var(--ease-linear) both; animation-timeline: view(x); }
|
|
206
191
|
@keyframes picker-focus {
|
|
207
192
|
0%, 100% { color: var(--_rest); }
|
|
208
193
|
45%, 55% { color: var(--card-ink); }
|
|
@@ -228,7 +213,7 @@
|
|
|
228
213
|
|
|
229
214
|
/* ---- bars: three or four bars with a count on top — the glance chart ----
|
|
230
215
|
<div class="bars"><div class="bar" style="--v: 16"><span>+16</span></div>… --max on the parent */
|
|
231
|
-
.bars { --max: 20; --_h:
|
|
216
|
+
.bars { --max: 20; --_h: calc(var(--tap-base) * 5 / 3); display: flex; align-items: end; gap: var(--bars-gap, var(--sp-2)); height: calc(var(--_h) + var(--sp-8)); }
|
|
232
217
|
.bar {
|
|
233
218
|
flex: 1;
|
|
234
219
|
min-width: 0;
|
|
@@ -238,7 +223,7 @@
|
|
|
238
223
|
gap: var(--bar-gap, var(--sp-1));
|
|
239
224
|
height: 100%;
|
|
240
225
|
font: var(--bar-font, var(--type-caption));
|
|
241
|
-
font-weight:
|
|
226
|
+
font-weight: var(--fw-bold);
|
|
242
227
|
}
|
|
243
228
|
.bar::after {
|
|
244
229
|
content: "";
|
|
@@ -254,8 +239,7 @@
|
|
|
254
239
|
<span class="spinner" role="status"><svg class="icon">…loader-circle…</svg>Loading…</span>
|
|
255
240
|
The ring turns; words beside it are optional and take the caption's look. Still when motion is reduced. */
|
|
256
241
|
.spinner { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2); vertical-align: middle; font: var(--type-caption); color: var(--spinner-ink, currentColor); }
|
|
257
|
-
.spinner > .icon {
|
|
258
|
-
@media (prefers-reduced-motion: no-preference) { .spinner > .icon { animation: turn 0.9s linear infinite; } } /* `turn`: tokens.css */
|
|
242
|
+
@media (prefers-reduced-motion: no-preference) { .spinner > .icon { --icon-spin: turn var(--motion-spin) var(--ease-linear) infinite; } } /* `turn`: tokens.css */
|
|
259
243
|
|
|
260
244
|
/* ---- progress: one straight line, the done share solid, the rest faint (Lh 2026-09-22) — a voice note's
|
|
261
245
|
timeline, a download, a step count as a line. <div class="progress" role="progressbar" style="--progress-at: 0.4"></div>
|
|
@@ -272,3 +256,24 @@
|
|
|
272
256
|
background-size: calc(var(--progress-at, 0) * 100%) 100%;
|
|
273
257
|
background-repeat: no-repeat;
|
|
274
258
|
}
|
|
259
|
+
|
|
260
|
+
/* ---- where it sits in what step.css draws (docs/reference.md rule 15) ---- */
|
|
261
|
+
/* a TRACK as the screen's TOP (Lh 2026-10-03): the walk's progress stands where a bar would, a row of the column, in the screen's
|
|
262
|
+
inset — and it never touches what comes up under it: the step (and so a sheet at full) starts --track-top-gap below it. That
|
|
263
|
+
room is the track's own, not the screen's gap; --track-top-lead is the air over it, under the status bar. Both = the screen's side inset (16): the pill
|
|
264
|
+
stands the same distance from everything around it (Lh 2026-10-03: 8 over was too little, 24 under too much). */
|
|
265
|
+
.screen--fill > .track:first-child {
|
|
266
|
+
flex: none;
|
|
267
|
+
margin-top: var(--track-top-lead, var(--sp-4));
|
|
268
|
+
margin-bottom: calc(var(--track-top-gap, var(--sp-4)) - var(--screen-gap));
|
|
269
|
+
}
|
|
270
|
+
/* …and over a STAGE (a map behind a sheet, Lh 2026-10-03): the stage is the whole screen, so the track is a LAYER on it — the map
|
|
271
|
+
shows around the pill — and the stage keeps the track's line free itself: a sheet at full stops --track-top-gap under the
|
|
272
|
+
track, as in a step. --top-h is the track's own height, measured by Screen (a numbered track grows with the text). */
|
|
273
|
+
.screen--fill > .track:first-child:has(+ .sheet-stage) {
|
|
274
|
+
position: absolute; z-index: var(--z-over);
|
|
275
|
+
inset: calc(var(--safe-top) + var(--track-top-lead, var(--sp-4))) var(--screen-pad) auto;
|
|
276
|
+
margin: 0;
|
|
277
|
+
}
|
|
278
|
+
/* ---- where it sits in what media.css draws (docs/reference.md rule 15) ---- */
|
|
279
|
+
.postcard__voice > .progress { justify-self: stretch; pointer-events: none; }
|
package/css/palette.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
|
-
cardds/palette.css —
|
|
2
|
+
cardds/palette.css — the palettes cardds ships. A project tunes its own theme.css (theme.template.css), never this file.
|
|
3
3
|
Apply with <html data-palette="clay"> (or <body>).
|
|
4
4
|
No attribute = wireframe grayscale from tokens.css.
|
|
5
5
|
Each palette overrides role tokens only — components never change.
|
package/css/people.css
CHANGED
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
font: var(--avatar-font, var(--type-label));
|
|
22
22
|
font-size: calc(var(--_size) * 0.3);
|
|
23
23
|
text-transform: uppercase;
|
|
24
|
-
letter-spacing:
|
|
24
|
+
letter-spacing: var(--track-loose);
|
|
25
25
|
position: relative;
|
|
26
26
|
text-decoration: none; /* .avatar works as <a> too */
|
|
27
27
|
}
|
|
28
28
|
.avatar > img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
|
|
29
|
-
.avatar > .icon {
|
|
29
|
+
.avatar > .icon { --icon-size: 50%; }
|
|
30
30
|
.avatar--sm { --_size: var(--avatar-sm); }
|
|
31
31
|
.avatar--lg { --_size: var(--avatar-lg); }
|
|
32
32
|
.avatar--xl { --_size: var(--avatar-xl); } /* sheet head icon ring */
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
inset: calc(-0.55 * var(--_size));
|
|
43
43
|
border-radius: 50%;
|
|
44
44
|
background: repeating-radial-gradient(circle, transparent 0 calc(var(--_size) * 0.5), currentColor calc(var(--_size) * 0.5) calc(var(--_size) * 0.5 + var(--border-w)), transparent calc(var(--_size) * 0.5 + var(--border-w)) calc(var(--_size) * 0.68));
|
|
45
|
-
opacity:
|
|
45
|
+
opacity: var(--fade-faint);
|
|
46
46
|
pointer-events: none;
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -51,11 +51,6 @@
|
|
|
51
51
|
.avatar-stack { display: inline-flex; align-items: center; }
|
|
52
52
|
.avatar-stack > .avatar { box-shadow: 0 0 0 var(--sp-1) var(--card-bg, var(--surface)); }
|
|
53
53
|
.avatar-stack > .avatar + .avatar { margin-left: calc(-1 * var(--sp-3)); }
|
|
54
|
-
/* a stamp in the stack is the stamp's small size (--stamp-sm-size: one rule in stamp.css, 48 × 60), lapped like the heads. Measured 2026-10-11 in a Row's
|
|
55
|
-
lead with a title, a meta line and an end button: at 375 the meta line breaks in two, accepted by Lh (a lap of 16 would hold it; the lap stays 8)
|
|
56
|
-
<span class="avatar-stack"><span class="stamp stamp--bare">…</span><span class="stamp stamp--bare">…</span><span class="avatar-stack__more">+9</span></span> */
|
|
57
|
-
.avatar-stack > .stamp { flex: none; margin-inline: 0; }
|
|
58
|
-
.avatar-stack > .stamp + .stamp { margin-left: calc(-1 * var(--sp-2)); }
|
|
59
54
|
/* more: the count that trails the stack — plain words on the ground, title size so it reads at arm's length (the accent "+7" avatar is the small, framed way) */
|
|
60
55
|
.avatar-stack__more { margin-left: var(--avatar-stack-more-gap, var(--sp-3)); font: var(--avatar-stack-more-font, var(--type-title)); color: var(--avatar-stack-more-ink, inherit); white-space: nowrap; }
|
|
61
56
|
|
package/css/post.css
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
.postmark {
|
|
20
20
|
--_base: calc(var(--tap) * 1.75);
|
|
21
21
|
position: relative;
|
|
22
|
-
z-index:
|
|
22
|
+
z-index: var(--z-over);
|
|
23
23
|
flex: none;
|
|
24
24
|
display: grid;
|
|
25
25
|
place-content: center;
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
color: var(--postmark-ink, color-mix(in srgb, currentColor 72%, transparent));
|
|
35
35
|
font: var(--postmark-font, var(--type-label));
|
|
36
36
|
text-align: center;
|
|
37
|
-
rotate: var(--postmark-tilt, -
|
|
37
|
+
rotate: var(--postmark-tilt, calc(var(--tilt-base) * -10));
|
|
38
38
|
}
|
|
39
39
|
.postmark::after { /* the cancel lines that run on over the stamp */
|
|
40
40
|
content: "";
|
|
@@ -59,9 +59,8 @@
|
|
|
59
59
|
display: grid;
|
|
60
60
|
width: var(--post-corner-stamp-w, var(--_w));
|
|
61
61
|
aspect-ratio: 4 / 5;
|
|
62
|
-
rotate: var(--post-corner-stamp-tilt,
|
|
62
|
+
rotate: var(--post-corner-stamp-tilt, calc(var(--tilt-base) * 2));
|
|
63
63
|
}
|
|
64
|
-
.post-corner__stamp > :is(.stamp, .stamp-place) { grid-area: 1 / 1; width: 100%; max-width: none; } /* the corner is the postcard's own size (72 × 90), outside the stamp's two sizes, by the owner's decision (2026-10-11); its box sizes the stamp, which fills it */
|
|
65
64
|
|
|
66
65
|
/* ============================================================
|
|
67
66
|
post-letter — READING one that arrived. The written side stood upright: the address half over the message half.
|
|
@@ -102,15 +101,15 @@
|
|
|
102
101
|
min-height: 0;
|
|
103
102
|
overflow: clip;
|
|
104
103
|
visibility: hidden;
|
|
105
|
-
rotate: calc(-1 * var(--post-letter-tilt,
|
|
104
|
+
rotate: calc(-1 * var(--post-letter-tilt, calc(var(--tilt-base) * 2)));
|
|
106
105
|
}
|
|
107
|
-
.post-letter__sheet--back { rotate: var(--post-letter-tilt,
|
|
106
|
+
.post-letter__sheet--back { rotate: var(--post-letter-tilt, calc(var(--tilt-base) * 2)); } /* turned over, the lean is the other way */
|
|
108
107
|
.post-letter--back > .post-letter__sheet--back,
|
|
109
108
|
.post-letter--picture > .post-letter__sheet--picture { visibility: visible; }
|
|
110
109
|
.post-letter:not(:has(> .post-letter__journey)) { --_room: 0rem; } /* no journey line: its room goes back to the sheet */
|
|
111
110
|
.post-letter__journey { grid-area: 2 / 1; margin: var(--sp-3) 0 0; text-align: center; font: var(--type-caption); color: var(--post-letter-journey-ink, var(--on-surface-muted)); }
|
|
112
111
|
|
|
113
|
-
/* the picture side: the print with its
|
|
112
|
+
/* the picture side: the print with its margin of paper, the place printed under it */
|
|
114
113
|
.post-letter__sheet--picture {
|
|
115
114
|
display: grid;
|
|
116
115
|
grid-template-rows: minmax(0, 1fr) auto;
|
|
@@ -129,7 +128,7 @@
|
|
|
129
128
|
.post-letter__sheet--back { display: flex; flex-direction: column; gap: var(--post-letter-gap, var(--sp-4)); padding: var(--post-letter-pad, var(--sp-5)); }
|
|
130
129
|
.post-letter__head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: var(--sp-2); padding-block-end: var(--sp-4); border-block-end: var(--rule-w) solid var(--_rule); }
|
|
131
130
|
.post-letter__to { display: grid; gap: var(--sp-2); min-width: 0; align-self: stretch; align-content: space-between; }
|
|
132
|
-
.post-letter__kicker { font: var(--type-caption); color: var(--_muted); letter-spacing:
|
|
131
|
+
.post-letter__kicker { font: var(--type-caption); color: var(--_muted); letter-spacing: var(--track-caps); }
|
|
133
132
|
.post-letter__lines { display: grid; gap: var(--sp-1); }
|
|
134
133
|
.post-letter__line { display: block; padding-block: var(--sp-1); border-block-end: var(--border-w) solid var(--_rule); font: var(--type-caption); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
135
134
|
.post-letter__line--name { font: var(--type-title); }
|
|
@@ -142,7 +141,7 @@
|
|
|
142
141
|
gap: var(--sp-3);
|
|
143
142
|
font: var(--post-letter-hand-font, var(--type-h2));
|
|
144
143
|
font-size: var(--post-letter-hand-font-size, var(--fs-lg));
|
|
145
|
-
line-height: var(--post-letter-hand-leading,
|
|
144
|
+
line-height: var(--post-letter-hand-leading, var(--lh-hand));
|
|
146
145
|
}
|
|
147
146
|
.post-letter__message > p { margin: 0; }
|
|
148
147
|
.post-letter__sign { align-self: flex-end; }
|
|
@@ -154,7 +153,7 @@
|
|
|
154
153
|
inset-block-start: calc(var(--_peek) / 2);
|
|
155
154
|
inset-inline-start: var(--sp-2);
|
|
156
155
|
translate: 0 -50%;
|
|
157
|
-
z-index: 2;
|
|
156
|
+
z-index: var(--z-over-2);
|
|
158
157
|
display: inline-flex;
|
|
159
158
|
align-items: center;
|
|
160
159
|
gap: var(--sp-2);
|
|
@@ -215,8 +214,8 @@
|
|
|
215
214
|
background: transparent;
|
|
216
215
|
color: inherit;
|
|
217
216
|
font: var(--post-desk-hand-font, var(--type-h2));
|
|
218
|
-
line-height: calc(var(--fs-body) * var(--post-desk-leading,
|
|
219
|
-
--_line: calc(var(--fs-body) * var(--post-desk-leading,
|
|
217
|
+
line-height: calc(var(--fs-body) * var(--post-desk-leading, var(--lh-ruled)));
|
|
218
|
+
--_line: calc(var(--fs-body) * var(--post-desk-leading, var(--lh-ruled)));
|
|
220
219
|
/* the ruled lines: one under every line of writing */
|
|
221
220
|
background-image: linear-gradient(to bottom, transparent calc(100% - var(--border-w)), var(--_rule) 0);
|
|
222
221
|
background-size: 100% var(--_line);
|
|
@@ -233,10 +232,10 @@
|
|
|
233
232
|
/* the stamp box: empty it is a place of dots to press; pressed, the chosen stamp lies in it */
|
|
234
233
|
.post-desk__slot { border: 0; background: transparent; color: inherit; }
|
|
235
234
|
@media (prefers-reduced-motion: no-preference) {
|
|
236
|
-
.post-desk__slot > .stamp {
|
|
237
|
-
.post-desk .postmark { animation: strike var(--stamp-on-arrive-time, var(--motion-sheet)) ease-in both; } /* struck as a StampOn is (`strike`: tokens.css), in the same time */
|
|
235
|
+
.post-desk__slot > .stamp { --stamp-arrive: post-desk-stick var(--motion-quick) var(--ease-out) both; }
|
|
236
|
+
.post-desk .postmark { animation: strike var(--stamp-on-arrive-time, var(--motion-sheet)) var(--ease-in) both; } /* struck as a StampOn is (`strike`: tokens.css), in the same time */
|
|
238
237
|
}
|
|
239
|
-
@keyframes post-desk-stick { from { translate: 0 calc(-1 * var(--sp-8)); rotate:
|
|
238
|
+
@keyframes post-desk-stick { from { translate: 0 calc(-1 * var(--sp-8)); rotate: calc(var(--tilt-base) * 14); opacity: 0; } }
|
|
240
239
|
/* the other half's showing edge brings it across: a tap-wide strip out to the screen's edge */
|
|
241
240
|
.post-desk__reach { position: absolute; inset-block: 0; width: var(--tap); border: 0; background: transparent; }
|
|
242
241
|
.post-desk__reach--address { inset-inline-end: calc(-1 * var(--screen-pad)); }
|
|
@@ -266,7 +265,7 @@
|
|
|
266
265
|
border-block-end: var(--rule-w) solid transparent;
|
|
267
266
|
background: transparent;
|
|
268
267
|
}
|
|
269
|
-
.post-desk__pick > .stamp {
|
|
268
|
+
.post-desk__pick > .stamp { --stamp-turn: var(--post-corner-stamp-tilt, calc(var(--tilt-base) * 2)); } /* in the tray a stamp keeps its words */
|
|
270
269
|
.post-desk__pick[aria-pressed="true"] { border-block-end-color: var(--on-surface); }
|
|
271
270
|
.post-desk__note { margin: 0; font: var(--type-caption); color: var(--post-desk-note-ink, var(--on-surface-muted)); min-height: 1.35em; }
|
|
272
271
|
/* what stands where the card was, once it has left */
|
|
@@ -291,7 +290,7 @@
|
|
|
291
290
|
margin-block-end: calc(var(--_e) - var(--_h));
|
|
292
291
|
display: grid;
|
|
293
292
|
grid-template-rows: var(--post-box-edge, var(--_edge)) minmax(0, 1fr);
|
|
294
|
-
transition: translate var(--motion-sheet) ease, rotate var(--motion-sheet) ease;
|
|
293
|
+
transition: translate var(--motion-sheet) var(--ease), rotate var(--motion-sheet) var(--ease);
|
|
295
294
|
}
|
|
296
295
|
.post-box {
|
|
297
296
|
--_edge: calc(var(--sp-base) * 20);
|
|
@@ -306,8 +305,8 @@
|
|
|
306
305
|
}
|
|
307
306
|
.post-box:has(> .post-box__card.is-open:not(:last-child)) { padding-block-end: var(--_part); }
|
|
308
307
|
.post-box__card:last-child { margin-block-end: 0; }
|
|
309
|
-
.post-box__card:nth-child(3n + 1) { rotate: var(--post-box-lean, 0.
|
|
310
|
-
.post-box__card:nth-child(3n + 2) { rotate: calc(var(--post-box-lean, 0.
|
|
308
|
+
.post-box__card:nth-child(3n + 1) { rotate: var(--post-box-lean, calc(var(--tilt-base) * 0.6)); }
|
|
309
|
+
.post-box__card:nth-child(3n + 2) { rotate: calc(var(--post-box-lean, calc(var(--tilt-base) * 0.6)) * -0.8); }
|
|
311
310
|
.post-box__card.is-open { rotate: none; }
|
|
312
311
|
.post-box__card.is-open ~ .post-box__card { translate: 0 var(--_part); }
|
|
313
312
|
.post-box__edge {
|
|
@@ -334,3 +333,8 @@
|
|
|
334
333
|
.post-box__card:not(.is-open) .postmark > span:last-child:not(:first-child) { display: none; }
|
|
335
334
|
.post-box__card:not(.is-open) .postmark { box-sizing: border-box; place-content: start center; padding-block-start: calc(var(--_size) * 0.18); }
|
|
336
335
|
@media (prefers-reduced-motion: reduce) { .post-box__card { transition: none; } }
|
|
336
|
+
|
|
337
|
+
/* ---- the desk's card leaves as a card with `leave` does (card.css LEAVE): the same two knobs, the same time ---- */
|
|
338
|
+
.post-desk__card { transition: translate var(--motion-sheet) var(--ease), rotate var(--motion-sheet) var(--ease); }
|
|
339
|
+
.post-desk--sent .post-desk__card { --_y: calc(var(--leave-rise, 160%) * -1); rotate: var(--leave-turn, calc(var(--tilt-base) * -5)); }
|
|
340
|
+
@media (prefers-reduced-motion: reduce) { .post-desk__card { transition: none; } }
|