@usableapp/cardds 0.7.15 → 0.7.17
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 +53 -0
- package/README.md +29 -28
- package/cardds.css +3 -3
- package/css/actions.css +67 -22
- package/css/base.css +44 -18
- package/css/card.css +25 -26
- package/css/choice.css +21 -33
- package/css/forms.css +0 -3
- package/css/journey.css +1 -3
- package/css/layover.css +19 -41
- package/css/lists.css +1 -6
- package/css/media.css +9 -18
- package/css/numbers.css +6 -21
- package/css/people.css +3 -9
- package/css/sheet.css +15 -19
- package/css/stack.css +21 -29
- package/css/step.css +7 -12
- package/css/theme.template.css +2 -1
- package/css/tokens.css +22 -4
- package/dist/actions/Btn.d.ts +1 -1
- package/dist/actions/Dropdown.js +3 -9
- package/dist/actions/Fab.d.ts +1 -1
- package/dist/actions/Fab.js +3 -2
- package/dist/cardds.css +264 -267
- package/dist/cards/Card.d.ts +24 -8
- package/dist/cards/Card.js +23 -8
- package/dist/cards/CardHead.d.ts +0 -1
- package/dist/cards/CardHead.js +1 -1
- package/dist/choice/Calendar.js +2 -5
- package/dist/choice/Mood.d.ts +10 -5
- package/dist/choice/Mood.js +4 -2
- package/dist/forms/Field.js +2 -3
- package/dist/forms/FileBtn.js +1 -1
- package/dist/forms/Pin.js +7 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/layover/Banner.d.ts +10 -3
- package/dist/layover/Banner.js +6 -3
- package/dist/lists/Row.d.ts +2 -1
- package/dist/lists/Row.js +5 -5
- package/dist/media/Postcard.js +3 -5
- package/dist/numbers/Band.d.ts +3 -1
- package/dist/numbers/Picker.js +3 -10
- package/dist/numbers/Track.d.ts +4 -2
- package/dist/numbers/Track.js +3 -3
- package/dist/people/AvatarPick.d.ts +3 -1
- package/dist/people/AvatarPick.js +2 -2
- package/dist/scaffold/ActionBar.d.ts +3 -3
- package/dist/scaffold/ActionBar.js +2 -2
- package/dist/scaffold/Centre.d.ts +2 -2
- package/dist/scaffold/Centre.js +2 -2
- package/dist/scaffold/PagerAt.d.ts +1 -1
- package/dist/scaffold/PagerAt.js +1 -1
- package/dist/scaffold/Screen.js +4 -7
- package/dist/scaffold/TopBar.js +3 -5
- package/dist/sheets/Drawer.js +3 -4
- package/dist/sheets/Sheet.js +9 -22
- package/dist/sheets/SheetStack.d.ts +2 -2
- package/dist/sheets/SheetStack.js +20 -13
- package/dist/sheets/SheetSteps.js +8 -11
- package/dist/sheets/StackBack.js +2 -1
- package/dist/sheets/StackSheet.d.ts +3 -2
- package/dist/sheets/StackSheet.js +7 -11
- package/dist/state.d.ts +24 -0
- package/dist/state.js +57 -0
- package/dist/templates/CallSheet.js +2 -4
- package/dist/templates/Home.d.ts +3 -2
- package/dist/templates/Home.js +2 -4
- package/dist/templates/Splash.js +3 -3
- package/dist/templates/Walkthrough.js +4 -5
- package/dist/type/Icon.js +3 -5
- package/dist/type/Text.d.ts +1 -1
- package/dist/type/textScale.d.ts +6 -0
- package/dist/type/textScale.js +12 -0
- package/package.json +7 -4
- package/dist/layover/Deck.d.ts +0 -9
- package/dist/layover/Deck.js +0 -10
package/css/card.css
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
cardds/card.css — the card boundary + list variants
|
|
3
|
-
One card = one boundary.
|
|
3
|
+
One card = one boundary. The base .card is tone 1; further tones
|
|
4
|
+
via .card--2/3/4/5 (role slots).
|
|
4
5
|
============================================================ */
|
|
5
6
|
|
|
6
7
|
.card {
|
|
@@ -25,14 +26,18 @@
|
|
|
25
26
|
.card > * { min-width: 0; }
|
|
26
27
|
|
|
27
28
|
.card--2 { --card-bg: var(--card-2-bg); --card-ink: var(--card-2-ink); --card-muted: var(--card-2-muted); }
|
|
28
|
-
.card--3 { --card-bg: var(--card-3-bg); --card-ink: var(--card-3-ink); }
|
|
29
|
-
.card--4 { --card-bg: var(--card-4-bg); --card-ink: var(--card-4-ink); --card-muted: var(--card-4-muted); }
|
|
30
|
-
.card--5 { --card-bg: var(--card-5-bg); --card-ink: var(--card-5-ink); --card-muted: var(--card-5-muted); }
|
|
31
|
-
|
|
32
29
|
/* dark/accent slot: accent-toned controls would vanish — the slot re-points
|
|
33
30
|
the accent tokens, so every accent consumer (btn--primary, chip--on,
|
|
34
31
|
check, toggle, ring, day…) inverts without per-component rules */
|
|
35
|
-
.card--3 { --accent: var(--card-3-ink); --on-accent: var(--card-3-bg); }
|
|
32
|
+
.card--3 { --card-bg: var(--card-3-bg); --card-ink: var(--card-3-ink); --accent: var(--card-3-ink); --on-accent: var(--card-3-bg); }
|
|
33
|
+
.card--4 { --card-bg: var(--card-4-bg); --card-ink: var(--card-4-ink); --card-muted: var(--card-4-muted); }
|
|
34
|
+
.card--5 { --card-bg: var(--card-5-bg); --card-ink: var(--card-5-ink); --card-muted: var(--card-5-muted); }
|
|
35
|
+
/* the dark slot's captions/hints/footnotes: --card-muted is tuned for a light card, so these dim the full ink
|
|
36
|
+
instead (one rule, matching specificity to what it replaces — .card--3 <space> <one class>) — merged from
|
|
37
|
+
base.css/choice.css/media.css/numbers.css/journey.css/people.css (2026-09-27 review). lists.css's .legend
|
|
38
|
+
differs (opacity 0.8, not --dim) and stays there. */
|
|
39
|
+
.card--3 :is(.t-muted, .calendar__wd, .composer__hint, .quote__by, .ring__label, .band__foot, .picker__unit,
|
|
40
|
+
.route__place, .note-row, .avatar-pick__role) { color: inherit; opacity: var(--dim); }
|
|
36
41
|
|
|
37
42
|
/* header row: title left, floating action right — centered, since the
|
|
38
43
|
trailing control (chip/badge/icon-btn/link) is almost always taller than
|
|
@@ -135,7 +140,7 @@
|
|
|
135
140
|
linear-gradient(to top left, transparent calc(50% - 1px), currentColor calc(50% - 1px), currentColor calc(50% + 1px), transparent calc(50% + 1px));
|
|
136
141
|
border: var(--border-w) solid currentColor;
|
|
137
142
|
border-radius: var(--r-media);
|
|
138
|
-
opacity:
|
|
143
|
+
opacity: var(--off);
|
|
139
144
|
min-height: 10rem;
|
|
140
145
|
}
|
|
141
146
|
.ph--sm { min-height: 7.5rem; }
|
|
@@ -161,8 +166,7 @@
|
|
|
161
166
|
one is there, and the screen's gap), or twice the room below it (the
|
|
162
167
|
bar's reserve) — whichever is less. --_lead is the title bar's height
|
|
163
168
|
only on a screen that HAS one: without a .topbar/.appbar before it the
|
|
164
|
-
block keeps that room (a
|
|
165
|
-
under it, need it on a short phone; Lh, 2026-09-11). */
|
|
169
|
+
block keeps that room (a card and its pager under it needs it on a short phone; Lh, 2026-09-11). */
|
|
166
170
|
--_lead: 0rem;
|
|
167
171
|
max-height: calc(2 * min(var(--screen-centre) - var(--_lead) - var(--screen-gap), 100% - var(--screen-centre) - var(--bar-reserve)));
|
|
168
172
|
overflow-y: auto;
|
|
@@ -173,9 +177,8 @@
|
|
|
173
177
|
|
|
174
178
|
/* ---- centred block: the same place, for one thing to read that is not a
|
|
175
179
|
lone card — an empty state, a card with its quiet second button and the
|
|
176
|
-
pager under it
|
|
177
|
-
edge and keeps the screen's inset as padding,
|
|
178
|
-
overhang and clip at the screen's edges (correct — the fold visual), and
|
|
180
|
+
pager under it. The block runs edge to
|
|
181
|
+
edge and keeps the screen's inset as padding, and
|
|
179
182
|
its children stack at the screen's own rhythm (Lh, 2026-09-11):
|
|
180
183
|
<div class="screen screen--fill"><header class="topbar">…</header>
|
|
181
184
|
<div class="screen__centre"><article class="card">…</article><button class="btn btn--quiet btn--block">…</button></div>
|
|
@@ -193,9 +196,9 @@
|
|
|
193
196
|
likes when not (Lh, 2026-09-12) ----
|
|
194
197
|
The member sees one content area — whatever is between the top bar and the
|
|
195
198
|
bar — and reads it as a whole page: a short card sits at its middle, a long
|
|
196
|
-
one simply makes the page longer, runs under the bar (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
+
one simply makes the page longer, runs under the bar (a sticky last row,
|
|
200
|
+
actions.css) and scrolls up like any page — nothing is capped, nothing
|
|
201
|
+
shrinks, no scroll box inside a box.
|
|
199
202
|
So on a .screen that is NOT --fill the group is in flow: the column is at
|
|
200
203
|
least one phone tall, the group takes the room after the top bar and
|
|
201
204
|
centres its children in it. The screen's own inset stays the inset.
|
|
@@ -213,9 +216,9 @@
|
|
|
213
216
|
the React adapter's slideTo() sets data-slide on :root, starts the transition
|
|
214
217
|
and updates inside it; a browser without them (the floor: Chrome 105 / iOS 16)
|
|
215
218
|
simply switches. Only the card is named; the rest of the page must not
|
|
216
|
-
cross-fade.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
+
cross-fade. The name is set by slideTo() on the moving element for the move only (2026-09-22): a static name on
|
|
220
|
+
every walk's current page made two walks on one page — the gallery — share it, and the browser aborts a
|
|
221
|
+
transition with a duplicate name. */
|
|
219
222
|
|
|
220
223
|
/* ---- pages: the cards of a walk, all one height ----
|
|
221
224
|
A sequence the member walks (an introduction) reads steadier when every card
|
|
@@ -248,14 +251,10 @@
|
|
|
248
251
|
animation-timing-function: ease-out;
|
|
249
252
|
mix-blend-mode: normal;
|
|
250
253
|
}
|
|
251
|
-
:root[data-slide="next"]::view-transition-old(centre-card-out) { animation-name: centre-card-out-left; }
|
|
252
|
-
:root[data-slide="next"]::view-transition-new(centre-card-in) { animation-name: centre-card-in-right; }
|
|
253
|
-
:root[data-slide="back"]::view-transition-old(centre-card-out) { animation-name: centre-card-out-right; }
|
|
254
|
-
:root[data-slide="back"]::view-transition-new(centre-card-in) { animation-name: centre-card-in-left; }
|
|
255
|
-
:root[data-slide="next"]::view-transition-old(screen-out) { animation-name: centre-card-out-left; }
|
|
256
|
-
:root[data-slide="next"]::view-transition-new(screen-in) { animation-name: centre-card-in-right; }
|
|
257
|
-
:root[data-slide="back"]::view-transition-old(screen-out) { animation-name: centre-card-out-right; }
|
|
258
|
-
:root[data-slide="back"]::view-transition-new(screen-in) { animation-name: centre-card-in-left; }
|
|
254
|
+
:root[data-slide="next"]::view-transition-old(centre-card-out), :root[data-slide="next"]::view-transition-old(screen-out) { animation-name: centre-card-out-left; }
|
|
255
|
+
:root[data-slide="next"]::view-transition-new(centre-card-in), :root[data-slide="next"]::view-transition-new(screen-in) { animation-name: centre-card-in-right; }
|
|
256
|
+
:root[data-slide="back"]::view-transition-old(centre-card-out), :root[data-slide="back"]::view-transition-old(screen-out) { animation-name: centre-card-out-right; }
|
|
257
|
+
:root[data-slide="back"]::view-transition-new(centre-card-in), :root[data-slide="back"]::view-transition-new(screen-in) { animation-name: centre-card-in-left; }
|
|
259
258
|
@keyframes centre-card-out-left { to { translate: -100% 0; opacity: 0; } }
|
|
260
259
|
@keyframes centre-card-out-right { to { translate: 100% 0; opacity: 0; } }
|
|
261
260
|
@keyframes centre-card-in-right { from { translate: 100% 0; opacity: 0; } }
|
package/css/choice.css
CHANGED
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
background: var(--check-bg, var(--action-bg));
|
|
22
22
|
display: grid;
|
|
23
23
|
place-items: center;
|
|
24
|
-
cursor: pointer;
|
|
25
24
|
}
|
|
26
25
|
.check::after {
|
|
27
26
|
content: "";
|
|
@@ -34,15 +33,14 @@
|
|
|
34
33
|
}
|
|
35
34
|
.check:checked { background: var(--accent); border-color: transparent; }
|
|
36
35
|
.check:checked::after { opacity: 1; }
|
|
37
|
-
.check:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
38
36
|
.check--sm { width: var(--check-sm-size, var(--tap-sm)); height: var(--check-sm-size, var(--tap-sm)); }
|
|
39
37
|
|
|
40
38
|
/* ---- toggle: big switch ----
|
|
41
39
|
<input type="checkbox" role="switch" class="toggle" aria-label="Private"> */
|
|
42
40
|
.toggle {
|
|
43
41
|
--_w: var(--bar-h); /* 64 × 36, knob = height − 2 × pad */
|
|
44
|
-
--_h:
|
|
45
|
-
--_pad:
|
|
42
|
+
--_h: calc(var(--tap-base) * 3 / 4);
|
|
43
|
+
--_pad: calc(var(--tap-base) / 16);
|
|
46
44
|
appearance: none;
|
|
47
45
|
width: var(--_w);
|
|
48
46
|
height: var(--_h);
|
|
@@ -52,8 +50,7 @@
|
|
|
52
50
|
border: var(--border-w) solid var(--toggle-border, var(--outline));
|
|
53
51
|
background: var(--toggle-bg, var(--action-bg));
|
|
54
52
|
position: relative;
|
|
55
|
-
|
|
56
|
-
transition: background 0.2s ease;
|
|
53
|
+
transition: background-color var(--motion-press) ease-out, border-color var(--motion-press) ease-out;
|
|
57
54
|
}
|
|
58
55
|
.toggle::after {
|
|
59
56
|
content: "";
|
|
@@ -65,11 +62,10 @@
|
|
|
65
62
|
border-radius: 50%;
|
|
66
63
|
background: currentColor;
|
|
67
64
|
opacity: 0.45;
|
|
68
|
-
transition: translate
|
|
65
|
+
transition: translate var(--motion-quick) ease, opacity var(--motion-quick) ease;
|
|
69
66
|
}
|
|
70
67
|
.toggle:checked { background: var(--accent); border-color: transparent; }
|
|
71
68
|
.toggle:checked::after { translate: calc(var(--_w) - var(--_h)) 0; background: var(--on-accent); opacity: 1; }
|
|
72
|
-
.toggle:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
73
69
|
|
|
74
70
|
/* ---- chip pick: a chip that is a choice — a native radio/checkbox inside the label, the picked one filled with accent.
|
|
75
71
|
Full-width in a .chip-grid (one slot / many options), or hugging its word on a .chip-row with --sign (an interest
|
|
@@ -85,11 +81,12 @@
|
|
|
85
81
|
chip grid the whole point is equal, full-width pick targets, so stretch.
|
|
86
82
|
Matters most in scripts without word spaces (Thai): min-content is the
|
|
87
83
|
whole string, tracks get wide, and hugging chips leave a ragged grid. */
|
|
88
|
-
.chip-grid > .chip { justify-self: stretch; justify-content: center; min-height: var(--btn-h); padding-inline: var(--sp-2);
|
|
89
|
-
.chip--pick {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
.chip--pick
|
|
84
|
+
.chip-grid > .chip { justify-self: stretch; justify-content: center; min-height: var(--btn-h); padding-inline: var(--sp-2); }
|
|
85
|
+
.chip--pick { position: relative; } /* the input covers the whole chip, on a row as in a grid */
|
|
86
|
+
/* the hidden input, the same shape wherever a native radio/checkbox drives a picked look:
|
|
87
|
+
a chip, a mood dot, a tile (media.css) */
|
|
88
|
+
:is(.chip--pick, .mood__opt, .tile--pick) > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: inherit; }
|
|
89
|
+
:is(.chip--pick, .tile--pick):has(:checked) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
93
90
|
/* --sign: the trailing + that turns 45° into an × when picked — one mask, one rotate, no swap (Lh 2026-09-16) */
|
|
94
91
|
.chip--sign { padding-right: var(--sp-2); }
|
|
95
92
|
.chip--sign::after {
|
|
@@ -122,11 +119,8 @@
|
|
|
122
119
|
padding-block: var(--day-pad-y, var(--sp-3)); /* equal air top and bottom, so the selected pill
|
|
123
120
|
is the same shape on every day */
|
|
124
121
|
padding-inline: 0;
|
|
125
|
-
border: 0;
|
|
126
122
|
border-radius: var(--day-radius, var(--r-chip));
|
|
127
|
-
|
|
128
|
-
color: inherit;
|
|
129
|
-
opacity: 0.6;
|
|
123
|
+
opacity: var(--rest);
|
|
130
124
|
}
|
|
131
125
|
.day__name { font: var(--day-name-font, var(--type-caption)); }
|
|
132
126
|
.day__num { font: 700 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
|
|
@@ -139,17 +133,13 @@
|
|
|
139
133
|
.calendar__head { display: flex; align-items: center; justify-content: space-between; gap: var(--calendar-head-gap, var(--sp-2)); }
|
|
140
134
|
.calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--calendar-grid-gap, var(--sp-1)); justify-items: center; }
|
|
141
135
|
.calendar__wd { font: var(--calendar-wd-font, var(--type-caption)); color: var(--calendar-wd-ink, var(--card-muted)); padding-block: var(--calendar-wd-pad-y, var(--sp-2)); }
|
|
142
|
-
.card--3 .calendar__wd { color: inherit; opacity: 0.7; }
|
|
143
136
|
.calendar__day {
|
|
144
137
|
width: 100%;
|
|
145
138
|
max-width: var(--calendar-day-max-w, var(--tap));
|
|
146
139
|
aspect-ratio: 1;
|
|
147
140
|
display: grid;
|
|
148
141
|
place-items: center;
|
|
149
|
-
border: 0;
|
|
150
142
|
border-radius: 50%;
|
|
151
|
-
background: transparent;
|
|
152
|
-
color: inherit;
|
|
153
143
|
font: 500 var(--fs-title)/1 var(--font-ui);
|
|
154
144
|
font-variant-numeric: tabular-nums;
|
|
155
145
|
grid-column-start: var(--start, auto); /* --start on the 1st: weekday column 1–7 */
|
|
@@ -172,8 +162,7 @@
|
|
|
172
162
|
display: flex;
|
|
173
163
|
justify-content: space-between;
|
|
174
164
|
}
|
|
175
|
-
.mood__opt { flex: 1 1 0; max-width: var(--mood-opt-max-w, var(--tap)); min-width: 0; height: var(--mood-opt-h, var(--tap)); display: grid; place-items: center; position: relative;
|
|
176
|
-
.mood__opt > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
165
|
+
.mood__opt { flex: 1 1 0; max-width: var(--mood-opt-max-w, var(--tap)); min-width: 0; height: var(--mood-opt-h, var(--tap)); display: grid; place-items: center; position: relative; }
|
|
177
166
|
.mood__dot {
|
|
178
167
|
width: var(--mood-dot-size, var(--icon-xs));
|
|
179
168
|
height: var(--mood-dot-size, var(--icon-xs));
|
|
@@ -204,12 +193,11 @@
|
|
|
204
193
|
text-align: center;
|
|
205
194
|
font: var(--pin-cell-font, var(--type-stat));
|
|
206
195
|
font-variant-numeric: tabular-nums;
|
|
196
|
+
caret-color: transparent; /* 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
|
|
197
|
+
edge. The sign is INSIDE the pill: a full-ink border and a blinking bar drawn where the digit will land (the native caret
|
|
198
|
+
is hidden — its height and place are the browser's; the pill's bar is one rule). A filled cell selects its digit instead. */
|
|
207
199
|
}
|
|
208
200
|
.pin__cell:not(:placeholder-shown) { color: var(--pin-cell-filled-ink, var(--accent)); } /* a typed digit: coloured words on the cell's own ground — no fill (Lh 2026-09-24; was an accent-filled pill) */
|
|
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
201
|
.pin--secret .pin__cell { -webkit-text-security: disc; text-security: disc; } /* a PIN: a dot per digit, the number pad kept (Lh 2026-09-25) */
|
|
214
202
|
.pin__cell:focus { outline: none; border-color: var(--pin-cell-active-border, currentColor); }
|
|
215
203
|
.pin__cell:focus:placeholder-shown {
|
|
@@ -239,10 +227,9 @@
|
|
|
239
227
|
letter-spacing: var(--track-h2);
|
|
240
228
|
outline: none;
|
|
241
229
|
}
|
|
242
|
-
.composer__input::placeholder { color: inherit; opacity:
|
|
230
|
+
.composer__input::placeholder { color: inherit; opacity: var(--off); }
|
|
243
231
|
.composer__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--composer-bar-gap, var(--sp-3)); }
|
|
244
232
|
.composer__hint { font: var(--composer-hint-font, var(--type-caption)); color: var(--composer-hint-ink, var(--card-muted)); }
|
|
245
|
-
.card--3 .composer__hint { color: inherit; opacity: 0.7; }
|
|
246
233
|
|
|
247
234
|
/* ---- slider: one value along a track, a thumb-size knob ----
|
|
248
235
|
<label class="slider"><span class="slider__label">ขนาดตัวหนังสือ</span><input class="slider__range" type="range" min="100" max="160" step="15" aria-label="ขนาดตัวหนังสือ"></label>
|
|
@@ -253,8 +240,8 @@
|
|
|
253
240
|
.slider { display: grid; gap: var(--slider-gap, var(--sp-1)); flex: 1; min-width: 0; position: relative; } /* relative: the glyph rides the knob */
|
|
254
241
|
/* ---- the sample glyph: one letter IN the knob, on-accent, at the type size
|
|
255
242
|
the slider sets — so a text-size slider shows the size before anything is
|
|
256
|
-
read (Lh, 2026-09-12).
|
|
257
|
-
as --slider-at (0..1) from the input's value; the glyph sits over the knob
|
|
243
|
+
read (Lh, 2026-09-12). The Slider component publishes the knob's place on
|
|
244
|
+
the .slider as --slider-at (0..1) from the input's value; the glyph sits over the knob
|
|
258
245
|
and never takes the pointer. <span class="slider__glyph" aria-hidden="true">ก</span>
|
|
259
246
|
before the input. */
|
|
260
247
|
.slider__glyph {
|
|
@@ -304,6 +291,8 @@
|
|
|
304
291
|
box-shadow: var(--shadow-float);
|
|
305
292
|
}
|
|
306
293
|
.slider__range:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
294
|
+
.slider__range:active::-webkit-slider-thumb { scale: var(--press-scale); } /* the knob gives under the finger like any control */
|
|
295
|
+
.slider__range:active::-moz-range-thumb { scale: var(--press-scale); }
|
|
307
296
|
|
|
308
297
|
/* ---- a switch that names itself (Toggle text, Lh 2026-09-23 — "อยู่บ้าน" on the home's top): the words, then the switch,
|
|
309
298
|
one label — the whole of it is the tap target ---- */
|
|
@@ -311,7 +300,6 @@
|
|
|
311
300
|
display: inline-flex; align-items: center; gap: var(--toggle-field-gap, var(--sp-2));
|
|
312
301
|
min-height: var(--tap);
|
|
313
302
|
font: var(--toggle-field-font, var(--type-label));
|
|
314
|
-
|
|
315
|
-
-webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
|
|
303
|
+
-webkit-user-select: none; user-select: none;
|
|
316
304
|
flex: none;
|
|
317
305
|
}
|
package/css/forms.css
CHANGED
|
@@ -44,9 +44,6 @@
|
|
|
44
44
|
display: inline-flex;
|
|
45
45
|
align-items: center;
|
|
46
46
|
gap: var(--add-row-gap, var(--sp-2));
|
|
47
|
-
border: 0;
|
|
48
|
-
background: none;
|
|
49
|
-
color: inherit;
|
|
50
47
|
font: var(--add-row-font, var(--type-label));
|
|
51
48
|
font-size: var(--add-row-font-size, var(--fs-body));
|
|
52
49
|
padding: var(--add-row-pad-y, var(--sp-2)) var(--add-row-pad-x, 0);
|
package/css/journey.css
CHANGED
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
.route__point--end .route__code::after { content: none; }
|
|
27
27
|
.route__time { font: var(--route-time-font, var(--type-body)); font-variant-numeric: tabular-nums; }
|
|
28
28
|
.route__place { font: var(--route-place-font, var(--type-caption)); color: var(--route-place-ink, var(--card-muted)); }
|
|
29
|
-
.card--3 .route__place { color: inherit; opacity: 0.7; }
|
|
30
29
|
|
|
31
30
|
.route__via { grid-column: 2; grid-row: 2; align-self: center; display: flex; align-items: center; opacity: 0.65; font-size: var(--route-via-font-size, var(--fs-body)); }
|
|
32
31
|
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
background: var(--tile-badge-bg, var(--card-3-bg));
|
|
41
40
|
color: var(--tile-badge-ink, var(--card-3-ink));
|
|
42
41
|
}
|
|
43
|
-
.tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity:
|
|
42
|
+
.tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity: var(--dim); }
|
|
44
43
|
.tile-badge__value { font: 800 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
|
|
45
44
|
|
|
46
45
|
/* plain: no fill — a date box beside a title (Jun / 18) */
|
|
@@ -58,4 +57,3 @@
|
|
|
58
57
|
font: var(--note-row-font, var(--type-caption));
|
|
59
58
|
color: var(--note-row-ink, var(--card-muted));
|
|
60
59
|
}
|
|
61
|
-
.card--3 .note-row { color: inherit; opacity: 0.7; }
|
package/css/layover.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
cardds/layover.css — cards that lay OVER other content:
|
|
3
3
|
Modal (ask) · popover menu · expanding banner · float bar ·
|
|
4
|
-
callout
|
|
4
|
+
callout. The sheet (peek / half / full / page)
|
|
5
5
|
lives in sheet.css; these compose with .sheet-stage.
|
|
6
6
|
============================================================ */
|
|
7
7
|
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
2. the drawer comes out of the card's bottom edge while the lift moves
|
|
25
25
|
up, both at once: the card grows from its centre, which never moves
|
|
26
26
|
(the modal centres itself — absolute, margin auto; a growing card keeps its middle where it is)
|
|
27
|
-
Closing:
|
|
28
|
-
reverse (
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
Closing: <Modal open onClosed> — set open={false} and the Modal component plays the
|
|
28
|
+
same animations in reverse (Animation.reverse() on each — nothing duplicated), then
|
|
29
|
+
calls onClosed for the app to unmount the stage. Modal no longer knows its own
|
|
30
|
+
buttons (data-answer / the "cardds:modal" event are gone; the app's own onClick
|
|
31
|
+
sets open={false}).
|
|
31
32
|
Reduced motion: the finished state, no beats; closing fires at once. */
|
|
32
33
|
.modal {
|
|
33
34
|
--drawer-h: 14rem;
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
--accent: var(--card-3-ink); /* the one solid button inverts on the dark drawer */
|
|
73
74
|
--on-accent: var(--card-3-bg);
|
|
74
75
|
}
|
|
75
|
-
.modal__drawer .t-caption { opacity:
|
|
76
|
+
.modal__drawer .t-caption { opacity: var(--dim); }
|
|
76
77
|
.modal__drawer .icon-btn { background: transparent; color: inherit; border-color: var(--ink-rail); }
|
|
77
78
|
.modal__drawer .icon-btn--invert { background: var(--accent); color: var(--on-accent); border-color: transparent; width: var(--tap-lg); height: var(--tap-lg); }
|
|
78
79
|
@media (prefers-reduced-motion: no-preference) {
|
|
@@ -106,8 +107,8 @@
|
|
|
106
107
|
element the button belongs to (a card, a sheet), opened by an .icon-btn:
|
|
107
108
|
<button class="icon-btn icon-btn--sm" popovertarget="m1" aria-label="More">…</button>
|
|
108
109
|
<div class="menu menu--actions menu--end" id="m1" popover><button class="menu__item"><svg class="icon"/>Edit</button>…</div>
|
|
109
|
-
No item stays current there
|
|
110
|
-
trigger sits at the far right, so the panel grows leftwards. */
|
|
110
|
+
No item stays current there — an actions menu's items carry no aria-current, unlike
|
|
111
|
+
the dropdown's. .menu--end = the trigger sits at the far right, so the panel grows leftwards. */
|
|
111
112
|
.menu {
|
|
112
113
|
display: none; /* explicit closed state — never display on the base rule */
|
|
113
114
|
margin: 0;
|
|
@@ -133,18 +134,14 @@
|
|
|
133
134
|
min-height: var(--menu-item-min-h, var(--tap));
|
|
134
135
|
width: 100%;
|
|
135
136
|
padding: var(--menu-item-pad-y, 0) var(--menu-item-pad-x, var(--sp-3));
|
|
136
|
-
border: 0;
|
|
137
137
|
/* concentric with the panel: the card radius minus the panel's padding, so the
|
|
138
138
|
highlight's curve follows the outer corner (Lh 2026-09-16) */
|
|
139
139
|
border-radius: calc(var(--r-card) - var(--menu-pad, var(--sp-2)));
|
|
140
|
-
background: transparent;
|
|
141
|
-
color: inherit;
|
|
142
140
|
font: var(--menu-item-font, var(--type-title));
|
|
143
141
|
text-align: left;
|
|
144
|
-
cursor: pointer;
|
|
145
142
|
}
|
|
146
143
|
.menu__item:hover, .menu__item:focus-visible { background: var(--ink-tint); outline: none; }
|
|
147
|
-
.menu__item[aria-current]
|
|
144
|
+
.menu__item[aria-current] { background: var(--accent); color: var(--on-accent); }
|
|
148
145
|
@supports (anchor-name: --a) {
|
|
149
146
|
.menu[popover] {
|
|
150
147
|
position-area: block-end span-inline-end;
|
|
@@ -158,7 +155,7 @@
|
|
|
158
155
|
.menu[popover] {
|
|
159
156
|
opacity: 0;
|
|
160
157
|
translate: 0 calc(-1 * var(--sp-2));
|
|
161
|
-
transition: display
|
|
158
|
+
transition: display var(--motion-quick) allow-discrete, overlay var(--motion-quick) allow-discrete, opacity var(--motion-quick), translate var(--motion-quick);
|
|
162
159
|
}
|
|
163
160
|
.menu:popover-open { opacity: 1; translate: 0 0; }
|
|
164
161
|
.menu.\:popover-open { opacity: 1; translate: 0 0; }
|
|
@@ -179,19 +176,19 @@
|
|
|
179
176
|
padding-inline: var(--card-pad, var(--screen-pad));
|
|
180
177
|
}
|
|
181
178
|
.banner__summary {
|
|
182
|
-
list-style: none;
|
|
179
|
+
list-style: none;
|
|
183
180
|
display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
|
|
184
181
|
padding-block: var(--banner-summary-pad-y, calc(var(--sp-base) * 1.5)); font: var(--banner-summary-font, var(--type-caption)); font-weight: 400;
|
|
185
182
|
}
|
|
186
183
|
.banner__summary::-webkit-details-marker { display: none; }
|
|
187
|
-
.banner__summary .icon { transition: rotate
|
|
184
|
+
.banner__summary .icon { transition: rotate var(--motion-quick) ease; }
|
|
188
185
|
.banner[open] .banner__summary .icon { rotate: 180deg; }
|
|
189
186
|
.banner__body { display: grid; gap: var(--banner-body-gap, var(--sp-1)); padding-bottom: var(--sp-4); }
|
|
190
187
|
.banner::details-content { width: 100%; }
|
|
191
188
|
@supports (interpolate-size: allow-keywords) {
|
|
192
189
|
@media (prefers-reduced-motion: no-preference) {
|
|
193
190
|
.banner { interpolate-size: allow-keywords; }
|
|
194
|
-
.banner::details-content { block-size: 0; overflow: clip; transition: block-size
|
|
191
|
+
.banner::details-content { block-size: 0; overflow: clip; transition: block-size var(--motion-quick) ease, content-visibility var(--motion-quick) allow-discrete; }
|
|
195
192
|
.banner[open]::details-content { block-size: auto; }
|
|
196
193
|
}
|
|
197
194
|
}
|
|
@@ -208,16 +205,12 @@
|
|
|
208
205
|
display: flex;
|
|
209
206
|
gap: var(--float-bar-gap, var(--sp-2));
|
|
210
207
|
align-items: center;
|
|
211
|
-
z-index:
|
|
208
|
+
z-index: var(--z-bar);
|
|
212
209
|
}
|
|
213
210
|
/* in a fill screen (its `bottom`) the float bar floats over the SCREEN's foot, not the window's — it lies over the content
|
|
214
211
|
(a map, a stack), so it is not a row of the column; the screen is its box (Lh 2026-09-23) */
|
|
215
212
|
.screen--fill > .float-bar { position: absolute; }
|
|
216
|
-
.sheet-stage > .float-bar { position: absolute; padding-inline:
|
|
217
|
-
/* .screen's default bottom padding clears an .action-bar; a .float-bar is
|
|
218
|
-
taller (--bar-h + its own bottom offset), so reserve the difference. Any
|
|
219
|
-
parent that hosts the bar counts (see .action-bar's reserve, actions.css) */
|
|
220
|
-
:has(> .float-bar) .screen:not(.screen--fill) { padding-bottom: calc(var(--bar-h) + var(--sp-5) + var(--sp-6)); }
|
|
213
|
+
.sheet-stage > .float-bar { position: absolute; } /* padding-inline removed 2026-09-27: it hard-coded --sp-4 over the --float-bar-pad-x knob, so the knob had no effect here */
|
|
221
214
|
.float-bar__pill {
|
|
222
215
|
flex: 1;
|
|
223
216
|
min-width: 0;
|
|
@@ -242,21 +235,17 @@
|
|
|
242
235
|
gap: var(--float-bar-item-gap, var(--sp-0));
|
|
243
236
|
min-height: var(--float-bar-item-min-h, var(--tap-lg));
|
|
244
237
|
padding: var(--float-bar-item-pad, var(--sp-1));
|
|
245
|
-
border: 0;
|
|
246
238
|
border-radius: var(--float-bar-item-radius, var(--r-chip));
|
|
247
|
-
background: transparent;
|
|
248
|
-
color: inherit;
|
|
249
239
|
font: var(--float-bar-item-font, var(--type-caption));
|
|
250
240
|
text-decoration: none;
|
|
251
|
-
opacity:
|
|
241
|
+
opacity: var(--rest);
|
|
252
242
|
overflow: hidden;
|
|
253
243
|
white-space: nowrap;
|
|
254
244
|
text-overflow: ellipsis;
|
|
255
245
|
}
|
|
256
246
|
/* aria-current="false" is the correct way to mark the OTHER tabs, so the
|
|
257
247
|
bare attribute selector lights every one of them up */
|
|
258
|
-
.float-bar__item[aria-current]:not([aria-current="false"])
|
|
259
|
-
.float-bar__item.is-on { background: var(--accent); color: var(--on-accent); opacity: 1; }
|
|
248
|
+
.float-bar__item[aria-current]:not([aria-current="false"]) { background: var(--accent); color: var(--on-accent); opacity: 1; }
|
|
260
249
|
.float-bar > .icon-btn { width: var(--bar-h); height: var(--bar-h); box-shadow: var(--shadow-float); }
|
|
261
250
|
|
|
262
251
|
/* ---- callout: a small card with a tail — over a map, a media, a mascot ----
|
|
@@ -290,18 +279,7 @@
|
|
|
290
279
|
border-left: 0;
|
|
291
280
|
}
|
|
292
281
|
.callout__title { font: var(--callout-title-font, var(--type-title)); }
|
|
293
|
-
.callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity:
|
|
294
|
-
/* ---- deck: swipe cards; the last child is on top ----
|
|
295
|
-
<div class="deck"><article class="card">…</article><article class="card">…</article><article class="card card--fold">front</article></div>
|
|
296
|
-
<div class="deck__actions"><button class="icon-btn">×</button><button class="icon-btn icon-btn--invert">✓</button></div> */
|
|
297
|
-
.deck { display: grid; place-items: center; padding: var(--deck-pad-top, var(--sp-4)) var(--deck-pad-x, var(--sp-4)) var(--deck-pad-bottom, var(--sp-6)); }
|
|
298
|
-
.deck > .card { grid-area: 1 / 1; width: min(100%, 20rem); min-height: 12.5rem; transition: rotate 0.3s ease, translate 0.3s ease; }
|
|
299
|
-
.deck > .card:nth-last-child(2) { rotate: -4deg; translate: calc(-1 * var(--sp-2)) var(--sp-2); opacity: 0.85; }
|
|
300
|
-
.deck > .card:nth-last-child(3) { rotate: 5deg; translate: var(--sp-3) var(--sp-4); opacity: 0.6; }
|
|
301
|
-
.deck > .card:nth-last-child(n + 4) { display: none; }
|
|
302
|
-
.deck__actions { display: flex; justify-content: center; gap: var(--deck-actions-gap, var(--sp-8)); }
|
|
303
|
-
.deck__actions > .icon-btn { width: var(--bar-h); height: var(--bar-h); }
|
|
304
|
-
|
|
282
|
+
.callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity: var(--dim); }
|
|
305
283
|
/* ---- splash: the app's mark at the human centre while it boots (Lh 2026-09-24) ----
|
|
306
284
|
<section class="screen screen--fill splash"><div class="splash__group"><div class="splash__mark">{the app's lottie/img}</div>
|
|
307
285
|
<p class="splash__text">…</p></div></section>
|
package/css/lists.css
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
who liked a postcard, the rest of a list. An end chevron turns when open. The app owns the list.
|
|
41
41
|
<details class="row-expand"><summary class="row">…lead · body · end…</summary><div class="row-expand__more">…</div></details> */
|
|
42
42
|
.row-expand { display: block; }
|
|
43
|
-
.row-expand > summary { list-style: none;
|
|
43
|
+
.row-expand > summary { list-style: none; }
|
|
44
44
|
.row-expand > summary::-webkit-details-marker { display: none; }
|
|
45
45
|
.row-expand > summary > .icon:last-child { transition: rotate var(--motion-slide); }
|
|
46
46
|
.row-expand[open] > summary > .icon:last-child { rotate: 180deg; }
|
|
@@ -192,14 +192,9 @@
|
|
|
192
192
|
align-items: center;
|
|
193
193
|
gap: var(--link-gap, var(--sp-1));
|
|
194
194
|
min-height: var(--link-min-h, var(--tap-sm));
|
|
195
|
-
color: inherit;
|
|
196
195
|
text-decoration: none;
|
|
197
196
|
font: var(--link-font, var(--type-label));
|
|
198
197
|
font-size: var(--link-font-size, var(--fs-body));
|
|
199
|
-
border: 0;
|
|
200
|
-
background: transparent;
|
|
201
|
-
padding: 0;
|
|
202
|
-
cursor: pointer;
|
|
203
198
|
}
|
|
204
199
|
/* a SMALL link (Lh 2026-09-23): a thing tapped rarely — "details", a ctaLink — at the minor-link size (15) and 28px of height;
|
|
205
200
|
its 44px tap target stays, an invisible ::after around it */
|
package/css/media.css
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
--card-ink: var(--card-3-ink);
|
|
14
14
|
--accent: var(--card-3-ink);
|
|
15
15
|
--on-accent: var(--card-3-bg);
|
|
16
|
-
min-height:
|
|
16
|
+
min-height: var(--_cover-min-h);
|
|
17
17
|
align-content: space-between;
|
|
18
18
|
overflow: hidden;
|
|
19
19
|
isolation: isolate;
|
|
@@ -53,7 +53,6 @@
|
|
|
53
53
|
.quote__text { margin: 0; font: var(--quote-text-font, var(--type-h1)); letter-spacing: var(--track-h1); text-wrap: balance; }
|
|
54
54
|
.quote--sm .quote__text { font: var(--type-h2); letter-spacing: var(--track-h2); }
|
|
55
55
|
.quote__by { font: var(--quote-by-font, var(--type-caption)); color: var(--quote-by-ink, var(--card-muted)); }
|
|
56
|
-
.card--3 .quote__by { color: inherit; opacity: 0.7; }
|
|
57
56
|
|
|
58
57
|
/* ---- tile grid: big square tiles — icon top, label bottom ----
|
|
59
58
|
<div class="tile-grid"><button class="tile"><svg class="icon"/><span class="tile__label">Light</span></button>
|
|
@@ -79,20 +78,16 @@
|
|
|
79
78
|
font: inherit;
|
|
80
79
|
text-align: left;
|
|
81
80
|
position: relative;
|
|
82
|
-
cursor: pointer;
|
|
83
81
|
}
|
|
84
82
|
.tile__label { font: var(--tile-label-font, var(--type-title)); }
|
|
85
83
|
.tile__value { font: var(--tile-value-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
|
|
86
|
-
.tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity:
|
|
84
|
+
.tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity: var(--dim); }
|
|
87
85
|
.tile__text { display: grid; gap: var(--tile-text-gap, var(--sp-0)); }
|
|
88
86
|
.tile--on { background: var(--tile-on-bg, var(--accent)); color: var(--tile-on-ink, var(--on-accent)); border-color: transparent; }
|
|
89
|
-
.tile--pick
|
|
90
|
-
.tile--pick:has(:checked) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
91
|
-
.tile--pick:has(:focus-visible) { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
87
|
+
/* .tile--pick's hidden input and :has(:checked) fill are declared once in choice.css, alongside .chip--pick and .mood__opt */
|
|
92
88
|
.tile--wide { grid-column: 1 / -1; }
|
|
93
89
|
/* stat: READ, not pressed — no frame of its own inside a card (a frame says "tap me"): icon · value · words on the card's ground */
|
|
94
|
-
.tile--stat { border-color: transparent; background: none; padding-inline: 0;
|
|
95
|
-
.tile--stat { min-height: var(--tile-stat-min-h, var(--avatar-xl)); gap: var(--tile-stat-gap, var(--sp-3)); grid-template-columns: auto 1fr; align-items: center; }
|
|
90
|
+
.tile--stat { border-color: transparent; background: none; padding-inline: 0; min-height: var(--tile-stat-min-h, var(--avatar-xl)); gap: var(--tile-stat-gap, var(--sp-3)); grid-template-columns: auto 1fr; align-items: center; }
|
|
96
91
|
/* upload: dashed, centred plus + label */
|
|
97
92
|
.tile--upload {
|
|
98
93
|
border-style: dashed;
|
|
@@ -115,7 +110,7 @@
|
|
|
115
110
|
the corner read as a notch with a line through it rather than a lifted flap.
|
|
116
111
|
The COLOUR goes transparent rather than the border going to 0, so the box
|
|
117
112
|
keeps its geometry and a fold card still measures the same as the cards
|
|
118
|
-
beside it
|
|
113
|
+
beside it. */
|
|
119
114
|
.card--fold {
|
|
120
115
|
border-color: transparent;
|
|
121
116
|
border-bottom-right-radius: 0;
|
|
@@ -169,8 +164,8 @@
|
|
|
169
164
|
overflow: hidden;
|
|
170
165
|
}
|
|
171
166
|
.mosaic > :first-child { grid-row: span 2; }
|
|
172
|
-
.mosaic > * { min-height: 0; border-radius: 0;
|
|
173
|
-
.mosaic > .ph { opacity:
|
|
167
|
+
.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 */
|
|
168
|
+
.mosaic > .ph { opacity: var(--off); border-width: var(--border-w); }
|
|
174
169
|
.mosaic > img { width: 100%; height: 100%; object-fit: cover; }
|
|
175
170
|
|
|
176
171
|
/* ---- postcard: a member's introduction in the shape of a cover card — the photo fills it,
|
|
@@ -199,7 +194,7 @@
|
|
|
199
194
|
liked it, a voice note, a reply live OUTSIDE the postcard, in a Row under it. */
|
|
200
195
|
.postcard {
|
|
201
196
|
position: relative;
|
|
202
|
-
min-height: var(--postcard-min-h,
|
|
197
|
+
min-height: var(--postcard-min-h, var(--_cover-min-h));
|
|
203
198
|
perspective: 60rem;
|
|
204
199
|
border-radius: var(--postcard-radius, var(--r-card));
|
|
205
200
|
isolation: isolate;
|
|
@@ -225,14 +220,10 @@
|
|
|
225
220
|
display: grid;
|
|
226
221
|
align-content: start;
|
|
227
222
|
padding: var(--postcard-face-pad, var(--card-pad-rest));
|
|
228
|
-
border: 0;
|
|
229
223
|
border-radius: inherit;
|
|
230
|
-
background: transparent;
|
|
231
|
-
color: inherit;
|
|
232
224
|
text-align: left;
|
|
233
|
-
cursor: pointer;
|
|
234
225
|
}
|
|
235
|
-
.postcard__turn:focus-visible { outline
|
|
226
|
+
.postcard__turn:focus-visible { outline-offset: calc(-1 * var(--sp-1)); } /* the ring inside the card's edge */
|
|
236
227
|
/* the front: the photo (or the wireframe box) fills it; the band reads on a scrim */
|
|
237
228
|
.postcard__front { background: var(--postcard-front-bg, var(--card-3-bg)); color: var(--postcard-front-ink, var(--card-3-ink)); }
|
|
238
229
|
.postcard__front .postcard__turn > img,
|