@usableapp/cardds 0.7.105 → 0.7.107
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 +134 -0
- package/bin/cardds.mjs +6 -2
- package/cardds.css +1 -0
- package/css/actions.css +16 -9
- package/css/base.css +6 -9
- package/css/card.css +21 -173
- package/css/finish.css +4 -4
- package/css/journey.css +4 -1
- package/css/layover.css +4 -14
- package/css/lists.css +3 -18
- package/css/media.css +1 -7
- package/css/parts.css +380 -0
- package/css/sheet.css +21 -140
- package/css/stack.css +15 -26
- package/css/stamp.css +1 -1
- package/css/step.css +3 -3
- package/css/theme.template.css +1 -1
- package/css/tokens.css +5 -4
- package/dist/actions/BarNote.d.ts +2 -2
- package/dist/actions/BarNote.js +2 -2
- package/dist/actions/Btn.d.ts +2 -2
- package/dist/actions/Btn.js +1 -1
- package/dist/actions/BtnRow.d.ts +1 -1
- package/dist/actions/BtnRow.js +1 -1
- package/dist/actions/Chip.d.ts +7 -3
- package/dist/actions/Chip.js +5 -5
- package/dist/actions/IconBtn.d.ts +1 -1
- package/dist/actions/IconBtn.js +1 -1
- package/dist/actions/IconBtnGroup.d.ts +1 -1
- package/dist/cardds.css +486 -410
- package/dist/cards/Card.d.ts +6 -22
- package/dist/cards/Card.js +11 -22
- package/dist/cards/CardFeed.d.ts +1 -1
- package/dist/cards/CardFeed.js +5 -5
- package/dist/cards/CardParts.d.ts +0 -12
- package/dist/cards/CardParts.js +0 -12
- package/dist/cards/cardGo.js +1 -1
- package/dist/cards/cardGrounds.js +2 -2
- package/dist/catalog.json +1 -1
- package/dist/forms/Field.d.ts +1 -1
- package/dist/forms/Field.js +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +6 -5
- package/dist/lists/Row.d.ts +7 -22
- package/dist/lists/Row.js +6 -4
- package/dist/media/MapArea.d.ts +7 -3
- package/dist/media/MapArea.js +4 -3
- package/dist/media/Tile.d.ts +3 -7
- package/dist/media/Tile.js +5 -5
- package/dist/numbers/Spinner.d.ts +1 -1
- package/dist/numbers/Spinner.js +1 -1
- package/dist/parts/Body.d.ts +7 -0
- package/dist/parts/Body.js +11 -0
- package/dist/parts/End.d.ts +10 -0
- package/dist/parts/End.js +7 -0
- package/dist/parts/Foot.d.ts +17 -0
- package/dist/parts/Foot.js +11 -0
- package/dist/parts/Head.d.ts +15 -0
- package/dist/parts/Head.js +34 -0
- package/dist/parts/Lead.d.ts +10 -0
- package/dist/parts/Lead.js +9 -0
- package/dist/parts/Words.d.ts +13 -0
- package/dist/parts/Words.js +20 -0
- package/dist/parts/headCtx.d.ts +7 -0
- package/dist/parts/headCtx.js +9 -0
- package/dist/scaffold/EmptyState.d.ts +1 -1
- package/dist/scaffold/EmptyState.js +1 -1
- package/dist/scaffold/Screen.js +1 -1
- package/dist/sheets/Modal.d.ts +3 -7
- package/dist/sheets/Modal.js +3 -7
- package/dist/sheets/Sheet.d.ts +2 -2
- package/dist/sheets/Sheet.js +8 -9
- package/dist/sheets/SheetStack.d.ts +3 -3
- package/dist/sheets/SheetStack.js +4 -4
- package/dist/sheets/SheetSteps.d.ts +1 -1
- package/dist/sheets/SheetSteps.js +2 -2
- package/dist/sheets/StackBack.d.ts +1 -1
- package/dist/sheets/StackBack.js +1 -1
- package/dist/sheets/StackSheet.js +7 -7
- package/dist/sheets/sheetMeasure.js +3 -3
- package/dist/sheets/sheetStepsPile.js +2 -2
- package/dist/sheets/stackCard.js +6 -6
- package/dist/templates/CallSheet.js +6 -5
- package/dist/templates/Walkthrough.js +3 -2
- package/docs/reference.md +75 -36
- package/lib/catalog.mjs +2 -0
- package/lib/mcp.mjs +1 -1
- package/lint/rules.mjs +22 -8
- package/package.json +2 -1
- package/scripts/codemod-parts.mjs +324 -0
- package/dist/cards/CardFoot.d.ts +0 -15
- package/dist/cards/CardFoot.js +0 -9
- package/dist/cards/CardHead.d.ts +0 -21
- package/dist/cards/CardHead.js +0 -39
- package/dist/cards/Stat.d.ts +0 -19
- package/dist/cards/Stat.js +0 -14
- package/dist/sheets/SheetBody.d.ts +0 -5
- package/dist/sheets/SheetBody.js +0 -9
- package/dist/sheets/SheetHead.d.ts +0 -15
- package/dist/sheets/SheetHead.js +0 -6
package/css/card.css
CHANGED
|
@@ -42,87 +42,25 @@
|
|
|
42
42
|
.card--4 { --card-bg: var(--card-4-bg); --dark: var(--card-4-dark); }
|
|
43
43
|
.card--5 { --card-bg: var(--card-5-bg); --dark: var(--card-5-dark); }
|
|
44
44
|
|
|
45
|
-
/* header row: title left, floating action right — centered, since the
|
|
46
|
-
trailing control (chip/badge/icon-btn/link) is almost always taller than
|
|
47
|
-
a single line of title text; top-aligning them left the control visually
|
|
48
|
-
low against the title's optical center. A head with a genuinely tall,
|
|
49
|
-
multi-line title can opt back out with .card__head--top. */
|
|
50
|
-
.card__head {
|
|
51
|
-
display: flex;
|
|
52
|
-
flex-wrap: var(--card-head-wrap, wrap); /* the caption takes a line of its own — no wrapper div */
|
|
53
|
-
align-items: center;
|
|
54
|
-
justify-content: space-between;
|
|
55
|
-
column-gap: var(--card-head-col-gap, var(--sp-3));
|
|
56
|
-
row-gap: var(--card-head-row-gap, var(--sp-0));
|
|
57
|
-
min-height: var(--card-head-min-h, auto);
|
|
58
|
-
}
|
|
59
|
-
.card__head--top { align-items: flex-start; }
|
|
60
45
|
|
|
61
46
|
.card__media { border-radius: var(--card-media-radius, var(--r-media)); overflow: hidden; position: relative; } /* relative: corner badges */
|
|
62
|
-
.card__end { text-align: right; display: grid; gap: var(--card-end-gap, var(--sp-0)); justify-items: end; } /* trailing value + caption */
|
|
63
47
|
.card__media > img { width: 100%; height: auto; object-fit: cover; }
|
|
64
48
|
|
|
65
|
-
/* ---- the card skeleton: head · content · foot ----
|
|
66
|
-
Every card is a head (the heading left, hints / actions / a more-menu at
|
|
67
|
-
the far right), its content, and an optional foot of action buttons.
|
|
68
|
-
The head's leading child — the heading, or the div that wraps heading and
|
|
69
|
-
caption — takes the room, so a heading with nothing at the far right runs
|
|
70
|
-
all the way across; whatever trails it hugs its own width. Only the
|
|
71
|
-
heading (or the div that wraps it) is that leading child: a control that
|
|
72
|
-
leads a custom head — a chip, an avatar, a tile badge — keeps its own
|
|
73
|
-
width, else the rule stretched a chip into a bar and an avatar into an
|
|
74
|
-
ellipse (two agents and the wallet demo caught it, 2026-09-10). */
|
|
75
|
-
.card__head > :is(h1, h2, h3, div, p):first-child { flex: 1; min-width: 0; }
|
|
76
|
-
|
|
77
|
-
/* Skeleton head — the heading in a card's OWN head, and only there.
|
|
78
|
-
The heading wraps, no squeeze, up to three lines and cut there with an
|
|
79
|
-
ellipsis (Lh 2026-10-21: "มันควรจะ max สามบรรทัด" — a request's title ran to
|
|
80
|
-
four lines on a phone; reverses 2026-09-16's "no clamp"). The count is the knob
|
|
81
|
-
--card-head-title-lines. The group that holds heading ·
|
|
82
|
-
caption is the head's own wrapping row (above) — no wrapper element, no
|
|
83
|
-
height, only the row gap. */
|
|
84
|
-
.card__head :is(h1, h2, h3) {
|
|
85
|
-
overflow-wrap: anywhere;
|
|
86
|
-
display: -webkit-box;
|
|
87
|
-
-webkit-box-orient: vertical;
|
|
88
|
-
-webkit-line-clamp: var(--card-head-title-lines, 3);
|
|
89
|
-
line-clamp: var(--card-head-title-lines, 3);
|
|
90
|
-
overflow: hidden;
|
|
91
|
-
padding-block-start: var(--_mark-room); /* a stacked Thai mark rises over the first line's box: room inside the clip, given back outside */
|
|
92
|
-
margin-block-start: calc(-1 * var(--_mark-room));
|
|
93
|
-
--_mark-room: 0.2em;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/* a Stamp in a card's head (`Card end={<Stamp …/>}`): the words stand at the left, the stamp at the right BESIDE BOTH LINES — the heading and the
|
|
97
|
-
caption pair up beside it, not the stamp beside the heading alone with the caption left under it. The stamp is its own size (--stamp-size,
|
|
98
|
-
stamp.css); the head only places it.
|
|
99
|
-
<div class="card__head"><h2 class="t-h2">…</h2><span class="t-caption t-muted">…</span><span class="stamp">…</span></div> */
|
|
100
|
-
.card__head:has(> .stamp) { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
|
|
101
|
-
.card__head:has(> .stamp) > :is(h1, h2, h3) { grid-column: 1; grid-row: 1; align-self: end; }
|
|
102
|
-
|
|
103
49
|
/* a JourneyBadge at the edge of the card's own first head (the date box in the top-left or top-right corner) keeps its corner concentric
|
|
104
50
|
with the card's: the card's corner (--r-card) less the room the padding leaves round it. journey.css reads it behind the theme's --tile-badge-radius,
|
|
105
51
|
so a theme that sets that knob still wins. */
|
|
106
|
-
.card > .
|
|
52
|
+
:is(.card > .head:first-child, .card__face > .card__turn + .head) > .tile-badge:is(:first-child, :last-child) { --_badge-radius: calc(var(--r-card) - var(--card-pad)); } /* a turning card's face: its head follows the unseen turn button */
|
|
107
53
|
|
|
108
54
|
/* ---- paper bands: the two-tone card (`Card paper="bands"`, Lh 2026-10-19): the head and the foot are paper bands edge to
|
|
109
55
|
edge over the card's padding, no hairline; the middle between them keeps the card's tone, lightened (--card-middle-bg). The card
|
|
110
|
-
clips, so the bands keep its corners. A two-faced card (below) carries the look on each face
|
|
56
|
+
clips, so the bands keep its corners. A two-faced card (below) carries the look on each face, never on the turning box: a clip
|
|
57
|
+
there cut the face off at the card's edges while it turned, its near edge being nearer the eye than the box (2026-10-21). */
|
|
111
58
|
:is(.card--bands, .card--bands > .card__face) {
|
|
112
59
|
background: var(--card-middle-bg, color-mix(in srgb, var(--card-bg) 35%, var(--card-1-bg)));
|
|
113
60
|
--dark: var(--card-1-dark); /* the middle is mostly paper: its words take the paper's side */
|
|
114
61
|
overflow: hidden;
|
|
115
62
|
}
|
|
116
|
-
|
|
117
|
-
--card-bg: var(--card-1-bg);
|
|
118
|
-
--dark: var(--card-1-dark);
|
|
119
|
-
background: var(--card-bg);
|
|
120
|
-
color: var(--card-ink);
|
|
121
|
-
margin-inline: calc(-1 * var(--card-pad));
|
|
122
|
-
padding-inline: var(--card-pad);
|
|
123
|
-
}
|
|
124
|
-
:is(.card--bands, .card--bands > .card__face) > .card__head { margin-top: calc(-1 * var(--card-pad)); padding-block: var(--card-pad); }
|
|
125
|
-
:is(.card--bands, .card--bands > .card__face) > .card__foot { margin-bottom: calc(-1 * var(--card-pad)); padding-block: var(--sp-3); } /* the foot band is slim */
|
|
63
|
+
.card--bands.card--turns { background: none; overflow: visible; } /* the turning box keeps --dark: the faces' ink is computed from it */
|
|
126
64
|
|
|
127
65
|
/* ---- two faces: the WHOLE card turns (`Card back`, Lh 2026-10-19). The article is the turning box (.flip, base.css) and only that;
|
|
128
66
|
each face is the card's paper — head · middle · foot — and both lie in one grid cell. The head and the foot are the same on both
|
|
@@ -144,10 +82,10 @@
|
|
|
144
82
|
position: relative;
|
|
145
83
|
}
|
|
146
84
|
.card__face > * { min-width: 0; }
|
|
147
|
-
.card__middle { flex: 1 0 auto; display:
|
|
85
|
+
.card__middle { flex: 1 0 auto; display: flex; flex-direction: column; gap: var(--card-gap); height: var(--card-middle-h, auto); overflow: hidden; } /* a flex column, not a grid: the middle's height is the flexed one, so one thing on a face (a QR, a code) may take the whole middle with height: 100% (a grid's auto row never stretched to it, and the thing stood at the top of an empty band) */
|
|
148
86
|
.card__turn { position: absolute; inset: 0; width: var(--px); height: var(--px); padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); }
|
|
149
87
|
.card__face:has(> .card__turn:focus-visible) { outline: var(--focus); outline-offset: calc(-1 * var(--sp-1)); }
|
|
150
|
-
.
|
|
88
|
+
.head > .chip-row > .chip > .icon { --icon-display: inline-block; --icon-align: -0.15em; --icon-margin: 0 var(--chip-sm-gap, var(--sp-1)) 0 0; }
|
|
151
89
|
|
|
152
90
|
/* ---- bleed: a scrolling track that runs to the container's edges ----
|
|
153
91
|
For a horizontal strip inside a padded container — .avatar-pick, .chip-row,
|
|
@@ -168,7 +106,7 @@
|
|
|
168
106
|
nothing jumps. Chromium/Safari paint it through the ::-webkit-scrollbar
|
|
169
107
|
parts; Firefox gets its thin bar in the same ink. */
|
|
170
108
|
.bleed {
|
|
171
|
-
margin-inline: calc(-1 * var(--card-pad, 0rem));
|
|
109
|
+
margin-inline: calc(-1 * var(--card-pad, 0rem)); /* the edge a bleeding child takes back from what holds it; .map--bleed (sheet.css) reads the same --card-pad */
|
|
172
110
|
padding-inline: var(--card-pad, 0rem);
|
|
173
111
|
padding-block-end: var(--sp-2);
|
|
174
112
|
scroll-padding-inline: var(--card-pad, 0rem);
|
|
@@ -185,20 +123,7 @@
|
|
|
185
123
|
}
|
|
186
124
|
/* a bleeding child sits over the container's rounded corner, so a strip that
|
|
187
125
|
is genuinely first or last needs the corner clipped for it */
|
|
188
|
-
.card:has(> .bleed:first-child), .card:has(> .bleed:last-child) { overflow: clip; }
|
|
189
|
-
|
|
190
|
-
/* foot: the card's action buttons on one line. One button takes the width,
|
|
191
|
-
up to four share it in even parts — so the gaps read even too — and from
|
|
192
|
-
three on they drop their side padding so the labels keep the room. */
|
|
193
|
-
.card__foot { display: flex; gap: var(--card-foot-gap, var(--sp-2)); align-items: center; color: var(--card-foot-ink, inherit); }
|
|
194
|
-
.card__foot:has(> :nth-child(3)) > .btn { --btn-pad-x: var(--sp-2); }
|
|
195
|
-
/* a small link under the buttons (CardFoot link — Lh 2026-09-23, as the ActionBar's ctaLink): the row wraps, the link takes a
|
|
196
|
-
whole short row of its own, centred — it never shares the buttons' line */
|
|
197
|
-
.card__foot--link { flex-wrap: wrap; align-content: end; row-gap: var(--card-foot-link-gap, var(--sp-1)); }
|
|
198
|
-
.card__foot-link { flex: 1 0 100%; display: flex; justify-content: center; }
|
|
199
|
-
/* round (CardFoot round): the foot holds a few round IconBtns, each its own size (they never share the line), as ONE group in the middle with
|
|
200
|
-
a gap that does not follow the card's width. The gap is wider than the Btns' (a miss between two round targets): --card-foot-gap sets it */
|
|
201
|
-
.card__foot--round { justify-content: center; gap: var(--card-foot-gap, var(--sp-6)); }
|
|
126
|
+
.card:has(> :is(.bleed, .map--bleed):first-child), .card:has(> :is(.bleed, .map--bleed):last-child) { overflow: clip; }
|
|
202
127
|
|
|
203
128
|
/* media placeholder for wireframes — currentColor so it survives any palette */
|
|
204
129
|
.ph {
|
|
@@ -316,7 +241,6 @@
|
|
|
316
241
|
align-items: center;
|
|
317
242
|
gap: var(--card-row-gap, var(--sp-3));
|
|
318
243
|
}
|
|
319
|
-
.card--row .card__body { flex: 1; display: grid; gap: var(--sp-0); }
|
|
320
244
|
|
|
321
245
|
/* ---- a short window of past entries (`CardList show`, Lh 2026-10-19): the list scrolls IN ITSELF, a few rows in sight and the next one cut
|
|
322
246
|
in half to say there is more. The height is counted in rows by the component (`--_card-list-h`, px: rows wrap to any height); the room
|
|
@@ -333,7 +257,7 @@
|
|
|
333
257
|
}
|
|
334
258
|
|
|
335
259
|
/* ---- the cross that takes a row off the list (`Card row onRemove`): small, at the row's top right corner, inside it ----
|
|
336
|
-
<label class="card card--row"><input type="radio">…<
|
|
260
|
+
<label class="card card--row"><input type="radio">…<span class="words">…</span><button class="card__remove" aria-label="Remove"><svg class="icon">x</svg></button></label>
|
|
337
261
|
Smaller than any button (a ring half a thing's size, `--tap-base` / 2 = 24) and it grows with the size of things; the touch target is an
|
|
338
262
|
unseen ring round it, never under --tap-floor (44). It stands in the row's flow, so the title wraps before it, tucked into the corner
|
|
339
263
|
by the row's own padding; it sits over the row's radio (which covers the whole card), so a touch on it removes and never picks. */
|
|
@@ -361,21 +285,10 @@
|
|
|
361
285
|
.card__remove > .icon { --icon-size: 62%; }
|
|
362
286
|
.card__remove:focus-visible { outline: var(--focus); outline-offset: var(--sp-0); }
|
|
363
287
|
|
|
364
|
-
/* leading thumb/icon in a row */
|
|
365
|
-
.card__thumb {
|
|
366
|
-
width: var(--card-thumb-size, var(--tap-sm)); height: var(--card-thumb-size, var(--tap-sm));
|
|
367
|
-
flex: none;
|
|
368
|
-
border-radius: var(--card-thumb-radius, var(--r-control));
|
|
369
|
-
border: var(--border-w) solid currentColor;
|
|
370
|
-
display: grid;
|
|
371
|
-
place-items: center;
|
|
372
|
-
font: var(--card-thumb-font, var(--type-label));
|
|
373
|
-
}
|
|
374
|
-
|
|
375
288
|
/* ---- card feed: a queue of cards, all in sight — one big, the rest small (Lh 2026-10-09, the whole queue 2026-10-15) ----
|
|
376
289
|
<div class="card-feed">
|
|
377
|
-
<article class="card feed-card"><div class="
|
|
378
|
-
<div class="feed-card__more"><div class="feed-card__rest">content · <div class="
|
|
290
|
+
<article class="card feed-card"><div class="head">lead · h2 · caption</div>
|
|
291
|
+
<div class="feed-card__more"><div class="feed-card__rest">content · <div class="foot">…</div></div></div></article>
|
|
379
292
|
<article class="card feed-card feed-card--small" role="button" tabindex="0">… <div class="feed-card__more" inert>…</div></article>
|
|
380
293
|
<p class="t-caption card-feed__end" role="note">the end</p>
|
|
381
294
|
</div>
|
|
@@ -386,9 +299,7 @@
|
|
|
386
299
|
shut content in the scrollable overflow of whatever holds the feed — and hidden once the row has shut. */
|
|
387
300
|
.card-feed { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; gap: var(--card-feed-gap, var(--card-gap-rest)); }
|
|
388
301
|
.feed-card { row-gap: 0; }
|
|
389
|
-
.feed-
|
|
390
|
-
.feed-card__head:has(> :is(.card__thumb, .feed-card__lead)) { grid-template-columns: auto minmax(0, 1fr); }
|
|
391
|
-
.feed-card__head:has(> .t-caption) > :is(.card__thumb, .feed-card__lead) { grid-row: span 2; }
|
|
302
|
+
.feed-card > .head:has(> .t-caption) > .feed-card__lead { grid-row: span 2; }
|
|
392
303
|
.feed-card__lead { display: grid; align-self: center; } /* a Stamp here is the small one, 48 × 60 (stamp.css) */
|
|
393
304
|
.card-feed__end { text-align: center; }
|
|
394
305
|
.feed-card__more { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr; }
|
|
@@ -405,22 +316,6 @@
|
|
|
405
316
|
.feed-card--small .feed-card__rest { transition: visibility 0s var(--motion-sheet); }
|
|
406
317
|
}
|
|
407
318
|
|
|
408
|
-
/* ---- inline stat block (revenue-style) ---- */
|
|
409
|
-
.stat { display: grid; gap: var(--stat-gap, var(--sp-1)); }
|
|
410
|
-
.stat--centre { text-align: center; justify-items: center; } /* the one number of a sheet or a stage, under a centred avatar */
|
|
411
|
-
.stat__row { display: flex; align-items: center; justify-content: space-between; gap: var(--stat-row-gap, var(--sp-3)); }
|
|
412
|
-
|
|
413
|
-
.delta {
|
|
414
|
-
font: var(--delta-font, var(--type-caption));
|
|
415
|
-
font-weight: var(--fw-bold);
|
|
416
|
-
padding: var(--delta-pad-y, var(--sp-1)) var(--delta-pad-x, var(--sp-2));
|
|
417
|
-
border-radius: var(--delta-radius, var(--r-chip));
|
|
418
|
-
background: var(--delta-bg, var(--card-3-bg));
|
|
419
|
-
--dark: var(--card-3-dark);
|
|
420
|
-
color: var(--delta-ink, var(--card-ink));
|
|
421
|
-
}
|
|
422
|
-
.delta--neg { background: var(--delta-neg-bg, var(--neg)); --dark: var(--neg-dark); color: var(--delta-neg-ink, var(--card-ink)); }
|
|
423
|
-
|
|
424
319
|
/* ---- panes: a few panes side by side inside a card or a sheet, one in sight (Lh 2026-10-02) ----
|
|
425
320
|
<div class="panes" role="group"><div class="panes__rail"><div class="panes__pane">…</div>…</div><span class="pager__at">…</span></div>
|
|
426
321
|
The rail runs to the card's edges and snaps a pane at a time; each pane takes the inset back, so its content stands on the
|
|
@@ -444,7 +339,7 @@
|
|
|
444
339
|
|
|
445
340
|
/* ---- cover: the image IS the card; text over a scrim ----
|
|
446
341
|
<article class="card card--cover"><div class="ph" role="img"></div>
|
|
447
|
-
<div class="
|
|
342
|
+
<div class="head"><button class="icon-btn icon-btn--sm">♥</button></div>
|
|
448
343
|
<div><h2 class="t-h1">Art Institute</h2><p class="t-body">111 South Michigan Ave</p></div>
|
|
449
344
|
<div class="row">…meta…</div></article> */
|
|
450
345
|
.card--cover {
|
|
@@ -534,14 +429,6 @@
|
|
|
534
429
|
.sheet-stack > .card:nth-child(3) { margin-top: calc(2 * var(--stack-step)); }
|
|
535
430
|
.sheet-stack > .card:nth-child(4) { margin-top: calc(3 * var(--stack-step)); }
|
|
536
431
|
.sheet-stack > .card:nth-child(5) { margin-top: calc(4 * var(--stack-step)); }
|
|
537
|
-
/* stack headers: uniform — [back?] title left, icons right, one height,
|
|
538
|
-
same gap to content on every card */
|
|
539
|
-
.sheet-stack > .card > .card__head {
|
|
540
|
-
align-items: center;
|
|
541
|
-
justify-content: flex-start;
|
|
542
|
-
min-height: var(--tap);
|
|
543
|
-
}
|
|
544
|
-
.sheet-stack--display > .card > .card__head { align-items: start; min-height: 0; }
|
|
545
432
|
/* ============================================================
|
|
546
433
|
.sheet-stack--tap — tap a peeked sheet open; a back button returns it.
|
|
547
434
|
|
|
@@ -594,8 +481,7 @@
|
|
|
594
481
|
overscroll-behavior: contain;
|
|
595
482
|
}
|
|
596
483
|
.sheet-stack--tap > .card.is-open.is-dragging { transition: none; }
|
|
597
|
-
.sheet-stack--tap > .card
|
|
598
|
-
.sheet-stack--tap > .card.is-open:not(:has(> .sheet__body)) {
|
|
484
|
+
.sheet-stack--tap > .card.is-open:not(:has(> .body)) {
|
|
599
485
|
padding-top: calc(var(--card-pad) + var(--_refresh, 0rem));
|
|
600
486
|
transition: translate var(--motion-sheet) var(--ease), padding-top var(--motion-sheet) var(--ease);
|
|
601
487
|
}
|
|
@@ -631,56 +517,18 @@
|
|
|
631
517
|
only, the sheet's size is untouched. A pull on the head keeps them square until the finger lets go. */
|
|
632
518
|
.sheet-stack--flood > .card { transition: translate var(--motion-sheet) var(--ease), border-radius var(--motion-sheet) var(--ease); }
|
|
633
519
|
.sheet-stack--flood > .card.is-open { border-top-left-radius: 0; border-top-right-radius: 0; }
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
transition: padding-left var(--motion-sheet) var(--ease);
|
|
638
|
-
}
|
|
639
|
-
/* ---- a stacked sheet with a SheetBody has the ONE sheet shape (Lh 2026-09-23, the home layout): sheet head
|
|
640
|
-
pinned at the top · sheet body scrolls, as long as it needs · sheet foot (a CardFoot, the CTA) at the bottom.
|
|
641
|
-
The card becomes a flex column and stops scrolling itself — only the body does. A sheet with no SheetBody
|
|
520
|
+
/* ---- a stacked sheet with a Body has the ONE sheet shape (Lh 2026-09-23, the home layout): sheet head
|
|
521
|
+
pinned at the top · sheet body scrolls, as long as it needs · sheet foot (a Foot, the CTA) at the bottom.
|
|
522
|
+
The card becomes a flex column and stops scrolling itself — only the body does. A sheet with no Body
|
|
642
523
|
keeps the old way: the whole open card scrolls. ---- */
|
|
643
|
-
.sheet-stack--tap > .card:has(> .
|
|
644
|
-
.sheet-stack--tap > .card:has(> .
|
|
645
|
-
/* a paper stack's foot is paper to the card's edges, and it covers the tinted flex gap above it so the
|
|
646
|
-
button does not sit on a colour stripe. The head's gap stays: it is outside this foot. The button stays
|
|
647
|
-
where it was — the padding gives back what the bleed takes. */
|
|
648
|
-
.sheet-stack > .card.sheet--paper > .card__foot {
|
|
649
|
-
background: var(--action-bg);
|
|
650
|
-
color: var(--card-ink);
|
|
651
|
-
margin-top: calc(-1 * var(--card-gap));
|
|
652
|
-
margin-inline: calc(-1 * var(--card-pad));
|
|
653
|
-
margin-bottom: calc(-1 * var(--card-pad));
|
|
654
|
-
padding-top: var(--card-gap);
|
|
655
|
-
padding-inline: var(--card-pad);
|
|
656
|
-
padding-bottom: var(--card-pad);
|
|
657
|
-
}
|
|
658
|
-
.stack-sheet > .card__head { flex-wrap: nowrap; }
|
|
524
|
+
.sheet-stack--tap > .card:has(> .body) { display: flex; flex-direction: column; gap: var(--card-gap); }
|
|
525
|
+
.sheet-stack--tap > .card:has(> .body).is-open { overflow: clip; }
|
|
659
526
|
/* ---- the back button, SMALL (Lh 2026-09-23: "give the sheet head more room" — and then "a glyph alone does not read as
|
|
660
527
|
tappable"): a SMALL DISC — its ring kept, 32px instead of 44, the chevron in it at 20 — so the head gives it 32px + a gap
|
|
661
528
|
(was 56px). Its tap target stays 44px: an invisible ::after around the disc. ---- */
|
|
662
|
-
.sheet-stack--tap > .card.is-open > .card__head:has(> .sheet-stack__back:not(.sheet-stack__back--end)) { padding-left: var(--stack-back-room, calc(var(--_back-size) + var(--sp-2))); }
|
|
663
|
-
:is(.sheet-stack--tap > .card, .sheet-stack--tap > :not(.card, .sheet-stack__base) > .card) > .card__head:has(> .sheet-stack__back--end) { transition: padding-right var(--motion-sheet) var(--ease); }
|
|
664
|
-
.sheet-stack--tap > .card.is-open > .card__head:has(> .sheet-stack__back--end) { padding-right: var(--stack-back-room, calc(var(--_back-size) + var(--sp-2))); }
|
|
665
529
|
/* a stacked sheet is a control until it is open — tapped, never read into: no text selection (a drag, a double tap, a long press),
|
|
666
530
|
no grey flash, no iOS callout; its head stays one when open. Only the OPEN sheet's body is text to select (Lh 2026-09-23) */
|
|
667
|
-
.sheet-stack--tap > .card
|
|
668
|
-
/* ---- where it sits in what sheet.css draws (docs/reference.md rule 15) ---- */
|
|
669
|
-
.sheet > .card__foot {
|
|
670
|
-
position: absolute; inset: calc(-1 * var(--border-w)) 0 0; z-index: var(--z-over); /* the sheet's border box: the same height the sheet's own % reads */
|
|
671
|
-
align-items: end;
|
|
672
|
-
align-content: end;
|
|
673
|
-
padding: 0 var(--card-pad) calc(var(--card-pad) + var(--_safe-b-open));
|
|
674
|
-
background: linear-gradient(var(--card-bg), var(--card-bg)) bottom / 100% var(--_foot) no-repeat;
|
|
675
|
-
pointer-events: none;
|
|
676
|
-
translate: 0 calc(-1 * var(--_y) - var(--_drag, 0rem));
|
|
677
|
-
transition: translate var(--motion-sheet) var(--ease), visibility 0s;
|
|
678
|
-
}
|
|
679
|
-
.sheet.is-dragging > .card__foot { transition: none; }
|
|
680
|
-
/* at peek and away the foot is NOT held on the box's edge: it stands on the sheet's own bottom edge (no counter-slide), so it
|
|
681
|
-
LEAVES AND ARRIVES WITH ITS SHEET — solid the whole way, hidden only once the slide is over. It used to stay in place and
|
|
682
|
-
fade: the body slid through the strip behind a half-transparent button (timebank, Lh 2026-10-01: "ตัวหนังสือลอดใต้ปุ่ม"). */
|
|
683
|
-
:is(.sheet--peek, .sheet--away) > .card__foot { translate: 0 0; visibility: hidden; transition: translate var(--motion-sheet) var(--ease), visibility 0s var(--ease-linear) var(--motion-sheet); }
|
|
531
|
+
.sheet-stack--tap > .card { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
|
|
684
532
|
/* ---- where it sits in what step.css draws (docs/reference.md rule 15) ---- */
|
|
685
533
|
/* a WALK (Pages of Page) — Lh 2026-09-23, third call: no top; the picture and the words are ONE run that scrolls up together
|
|
686
534
|
(the picture is never pinned). Each page starts at the screen's lead and ends with the same room under it — the lead as
|
|
@@ -709,7 +557,7 @@
|
|
|
709
557
|
.postcard__front .postcard__turn > .ph { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border: 0; border-radius: 0; min-height: 0; }
|
|
710
558
|
|
|
711
559
|
/* ---- where it sits in what stack.css draws: a sheet pulled to refresh holds still (docs/reference.md rule 15) ---- */
|
|
712
|
-
.sheet-stack--tap > .card.is-open.is-refreshing:not(:has(> .
|
|
560
|
+
.sheet-stack--tap > .card.is-open.is-refreshing:not(:has(> .body)) { transition: none; }
|
|
713
561
|
|
|
714
562
|
/* ---- where it sits in what step.css draws: the reading block, a walk of pages (docs/reference.md rule 15) ---- */
|
|
715
563
|
.step:has(> .sheet--peek) > .pages { padding-bottom: var(--sheet-peek); }
|
package/css/finish.css
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
0 0.5rem 1rem -0.25rem color-mix(in srgb, var(--accent) 45%, transparent);
|
|
83
83
|
}
|
|
84
84
|
/* a peeked stack tab is the icon alone: no sheen and no shadow until its sheet is open (the fill fades with the sheet) */
|
|
85
|
-
[data-finish="lit"] .sheet-stack--tap > .card:not(.is-open) > .
|
|
85
|
+
[data-finish="lit"] .sheet-stack--tap > .card:not(.is-open) > .head .stack-sheet__tab {
|
|
86
86
|
background-image: none;
|
|
87
87
|
box-shadow: none;
|
|
88
88
|
}
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
}
|
|
122
122
|
/* a dark card (the brand tone, a cover): multiply would only blacken it, so the grain lightens, fainter */
|
|
123
123
|
[data-finish="print"] :is(.card--3, .card--cover)::after { mix-blend-mode: screen; opacity: calc(var(--print-grain, 0.32) * 0.56); }
|
|
124
|
-
[data-finish="print"] .card:not(.card--row, .card--cover, .feed-card--small, .sheet-stack > *, .sheet-stack > :not(.sheet-stack__base) > *) > .
|
|
124
|
+
[data-finish="print"] .card:not(.card--row, .card--cover, .feed-card--small, .sheet-stack > *, .sheet-stack > :not(.sheet-stack__base) > *) > .head {
|
|
125
125
|
padding-bottom: var(--sp-2);
|
|
126
126
|
border-bottom: var(--rule-w) dotted color-mix(in srgb, currentColor 45%, transparent);
|
|
127
127
|
}
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
[data-finish="print"] .chip { border-color: var(--chip-border, var(--_ink)); } /* a holder that clears a chip's edge (a note's chips) clears it in print too */
|
|
133
133
|
[data-finish="print"] .btn--primary { box-shadow: 0.125rem 0.125rem 0 0 color-mix(in srgb, var(--card-ink) 60%, transparent); }
|
|
134
134
|
[data-finish="print"] :is(.btn, .chip):active { box-shadow: none; translate: 0.125rem 0.125rem; }
|
|
135
|
-
[data-finish="print"] .
|
|
135
|
+
[data-finish="print"] .card--row > .lead { border-style: dashed; }
|
|
136
136
|
|
|
137
137
|
/* ============================================================
|
|
138
138
|
sticker — a die-cut sticker on a sheet: --sticker-cut of white paper left round the print, a short hard
|
|
@@ -167,4 +167,4 @@
|
|
|
167
167
|
[data-finish="ink"] :is(.btn, .chip, .icon-btn:not(.icon-btn-group > *)) { border: var(--ink-w, 0.15625rem) solid currentColor; box-shadow: 0.1875rem 0.1875rem 0 0 currentColor; }
|
|
168
168
|
[data-finish="ink"] .btn--primary { border-color: var(--on-surface); box-shadow: 0.1875rem 0.1875rem 0 0 var(--on-surface); }
|
|
169
169
|
[data-finish="ink"] .toggle { box-shadow: 0.125rem 0.125rem 0 0 var(--on-surface); }
|
|
170
|
-
[data-finish="ink"] .
|
|
170
|
+
[data-finish="ink"] .card--row > .lead { border-width: var(--ink-w, 0.15625rem); }
|
package/css/journey.css
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
over the number and the month on one line; tall (a two-line title) = three lines, day · number · month. It grows no taller than
|
|
55
55
|
its three lines (2026-10-21, timebank: beside a four-line title it was a tall empty block); held there, it stands at the head's top. */
|
|
56
56
|
.tile-badge--stretch {
|
|
57
|
+
line-height: var(--tile-badge-line, var(--lh-badge)); /* the knob's own property; the cap below reads it too */
|
|
57
58
|
align-self: stretch;
|
|
58
59
|
max-block-size: calc((2 * var(--fs-caption) + var(--fs-lg)) * var(--tile-badge-line, var(--lh-badge)) + 2 * var(--sp-3));
|
|
59
60
|
container-type: size;
|
|
@@ -65,7 +66,6 @@
|
|
|
65
66
|
column-gap: var(--sp-1);
|
|
66
67
|
row-gap: 0;
|
|
67
68
|
padding-block: var(--sp-3);
|
|
68
|
-
line-height: var(--tile-badge-line, var(--lh-badge));
|
|
69
69
|
}
|
|
70
70
|
.tile-badge--stretch > * { line-height: inherit; }
|
|
71
71
|
.tile-badge--stretch > :first-child { grid-column: 1 / -1; }
|
|
@@ -73,6 +73,9 @@
|
|
|
73
73
|
.tile-badge--stretch > * { grid-column: 1 / -1; }
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/* where it sits in a head that leads with the date (card.css): the first cell, as tall as the chips beside it however many rows, so no cap */
|
|
77
|
+
.head:has(> .tile-badge:first-child + .chip-row) > .tile-badge--stretch:first-child { grid-column: 1; grid-row: 1; max-block-size: none; }
|
|
78
|
+
|
|
76
79
|
/* corner badge: pin a tile badge on media (rating, price) — .card__media is relative */
|
|
77
80
|
.tile-badge--corner { position: absolute; right: var(--sp-3); bottom: var(--sp-3); }
|
|
78
81
|
|
package/css/layover.css
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
/* ---- the Modal — the case drops in, its drawer opens below (Lh 2026-09-16: ONE card, two parts)
|
|
9
9
|
<div class="sheet-stage sheet-stage--dim"> (dim = the page under a scrim; a Modal on a clean ground is the stage without it — Lh 2026-09-22, `--ask` retired: the stage does the ground, the modal centres itself)
|
|
10
10
|
<article class="card modal">
|
|
11
|
-
<div class="
|
|
11
|
+
<div class="body">…the card's content (a head, rows, a timeline)…</div>
|
|
12
12
|
<div class="modal__drawer"><h2 class="t-h2">Take the case?</h2><p class="t-caption">This cannot be undone.</p>
|
|
13
13
|
<div class="btn-trio">…</div>
|
|
14
14
|
</div>
|
|
15
15
|
</article>
|
|
16
16
|
</div>
|
|
17
17
|
.modal is the card — no border (the shadow is its edge on the scrim);
|
|
18
|
-
.
|
|
18
|
+
.body the light part that holds the thing being asked about (it
|
|
19
19
|
scrolls when tall); .modal__drawer the dark end that asks. The ground is
|
|
20
20
|
whatever the stage shows (dim it or not). Two beats (2 × --motion-sheet) when
|
|
21
21
|
the stage appears, and the same two backwards when it closes:
|
|
@@ -47,16 +47,6 @@
|
|
|
47
47
|
overflow: clip;
|
|
48
48
|
box-shadow: var(--modal-shadow, var(--shadow-sheet));
|
|
49
49
|
}
|
|
50
|
-
.modal__lift {
|
|
51
|
-
padding: var(--card-pad);
|
|
52
|
-
display: grid;
|
|
53
|
-
gap: var(--card-gap);
|
|
54
|
-
align-content: start;
|
|
55
|
-
flex: 0 1 auto;
|
|
56
|
-
min-height: 0;
|
|
57
|
-
overflow-y: auto; /* tall content scrolls inside; the drawer stays */
|
|
58
|
-
overscroll-behavior: contain;
|
|
59
|
-
}
|
|
60
50
|
.modal__drawer {
|
|
61
51
|
flex-shrink: 0;
|
|
62
52
|
height: var(--drawer-h);
|
|
@@ -81,8 +71,8 @@
|
|
|
81
71
|
--accent: var(--card-ink);
|
|
82
72
|
--accent-dark: calc(1 - var(--dark));
|
|
83
73
|
}
|
|
84
|
-
/* the CONFIRM: a Modal with no
|
|
85
|
-
.modal:not(:has(> .
|
|
74
|
+
/* the CONFIRM: a Modal with no Body: the ModalAsk IS the card, as tall as its words (no fixed band, no second beat) */
|
|
75
|
+
.modal:not(:has(> .body)) > .modal__drawer { height: auto; padding-block: var(--modal-confirm-pad-y, var(--sp-6)); animation: none; }
|
|
86
76
|
.modal__drawer .t-caption { --t-caption-ink: var(--modal-drawer-caption-ink, var(--card-muted)); }
|
|
87
77
|
.modal__drawer > h2:focus { outline: none; } /* the question takes the cursor when the modal arrives (for a screen reader, the keyboard's way in) — it is not a control: no ring */
|
|
88
78
|
/* the answers as WORD buttons (a BtnRow of Btns; Lh 2026-10-11): the row fills the ask with room over it and each button keeps its words on one
|
package/css/lists.css
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
cardds/lists.css — frameless rows and pairs INSIDE a card:
|
|
3
|
-
row (lead ·
|
|
4
|
-
timeline · legend · link
|
|
3
|
+
row (its parts, lead · words · end, are parts.css) · key-value
|
|
4
|
+
rows · kv grid · timeline · legend · link
|
|
5
5
|
A row inside a card has no boundary of its own; .rows draws
|
|
6
6
|
hairlines between rows. A row that IS a card = .card--row.
|
|
7
7
|
============================================================ */
|
|
8
8
|
|
|
9
9
|
/* ---- row: leading visual · title + meta · trailing control ----
|
|
10
|
-
<div class="row"><span class="avatar">A</span><
|
|
10
|
+
<div class="row"><span class="avatar">A</span><span class="words"><span class="words__title">Alice Kau</span><span class="words__meta">Owner</span></span><button class="icon-btn icon-btn--sm">›</button></div> */
|
|
11
11
|
.row {
|
|
12
12
|
display: flex;
|
|
13
13
|
align-items: center;
|
|
@@ -20,21 +20,7 @@
|
|
|
20
20
|
/* no padding on a row (Lh 2026-09-16): padding is for room, not for spreading.
|
|
21
21
|
Space BETWEEN rows is .rows' gap; a lone row as card content takes the card's
|
|
22
22
|
gap. (.row--flush, which zeroed the old padding, is gone — every row is flush.) */
|
|
23
|
-
.row__lead {
|
|
24
|
-
width: var(--row-lead-size, var(--tap));
|
|
25
|
-
height: var(--row-lead-size, var(--tap));
|
|
26
|
-
flex: none;
|
|
27
|
-
border-radius: var(--row-lead-radius, var(--r-control));
|
|
28
|
-
border: var(--border-w) solid var(--row-lead-border, var(--outline));
|
|
29
|
-
display: grid;
|
|
30
|
-
place-items: center;
|
|
31
|
-
}
|
|
32
|
-
.row__body { flex: 1; min-width: 0; display: grid; gap: var(--row-body-gap, var(--sp-0)); }
|
|
33
|
-
.row__title { font: var(--row-title-font, var(--type-title)); overflow-wrap: anywhere; } /* wraps freely — no clip, so no padding trick for Thai marks (Lh 2026-09-16, same call as the card heading) */
|
|
34
|
-
.row__meta { font: var(--row-meta-font, var(--type-caption)); color: var(--row-meta-ink, var(--card-muted)); display: flex; align-items: center; gap: var(--row-meta-gap, var(--sp-1)); }
|
|
35
|
-
.row__end { margin-left: auto; text-align: right; display: grid; gap: var(--row-end-gap, var(--sp-0)); justify-items: end; flex: none; }
|
|
36
23
|
.row--lg { min-height: var(--row-lg-min-h, var(--fab)); } /* menu rows: a taller target, round arrow at the end; .rows gives them more air */
|
|
37
|
-
.row--lg .row__title { font: var(--fw-bold) var(--fs-lg) / var(--lh-h1) var(--font-display); }
|
|
38
24
|
/* a row that EXPANDS (Lh 2026-09-22): the row is a <details>' summary, what it shows comes out under it —
|
|
39
25
|
who liked a postcard, the rest of a list. An end chevron turns when open. The app owns the list.
|
|
40
26
|
<details class="row-expand"><summary class="row">…lead · body · end…</summary><div class="row-expand__more">…</div></details> */
|
|
@@ -49,7 +35,6 @@
|
|
|
49
35
|
pointer-events off here, aria-disabled from the markup. The tick is the .check's checked look
|
|
50
36
|
at --tap-sm, but not an input: nothing to press. */
|
|
51
37
|
.row--done { pointer-events: none; cursor: default; }
|
|
52
|
-
.row--done .row__title { color: var(--card-muted); }
|
|
53
38
|
.row__done {
|
|
54
39
|
width: var(--row-done-size, var(--tap-sm));
|
|
55
40
|
height: var(--row-done-size, var(--tap-sm));
|
package/css/media.css
CHANGED
|
@@ -43,17 +43,11 @@
|
|
|
43
43
|
position: relative;
|
|
44
44
|
}
|
|
45
45
|
.tile__label { font: var(--tile-label-font, var(--type-title)); }
|
|
46
|
-
.tile__value { font: var(--tile-value-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
|
|
47
46
|
.tile__sub { font: var(--tile-sub-font, var(--type-caption)); color: var(--tile-sub-ink, var(--card-muted)); } /* the muted words of the tile's ground; --dim faded the full ink to 2.4–3.4:1 */
|
|
48
47
|
.tile__text { display: grid; gap: var(--tile-text-gap, var(--sp-0)); }
|
|
49
48
|
.tile--on { background: var(--tile-on-bg, var(--accent)); --dark: var(--accent-dark); color: var(--tile-on-ink, var(--card-ink)); border-color: transparent; }
|
|
50
49
|
/* .tile--pick's hidden input and :has(:checked) fill are declared once in choice.css, alongside .chip--pick and .mood__opt */
|
|
51
50
|
.tile--wide { grid-column: 1 / -1; }
|
|
52
|
-
/* 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 */
|
|
53
|
-
/* a stat tile has no fill of its own, so it has no ink of its own: it takes the ink of what it stands on (currentColor on `color`
|
|
54
|
-
= the inherited colour), as the card's heading does. It kept the plain tile's --action-ink, dark on a dark card (timebank, tone 3,
|
|
55
|
-
Lh 2026-10-18). --tile-ink still overrides; a toned stat tile (.tile--N, below) takes its fill and its ink back */
|
|
56
|
-
.tile--stat { --dark: inherit; color: var(--tile-ink, currentColor); 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; }
|
|
57
51
|
/* upload: dashed, centred plus + label */
|
|
58
52
|
.tile--upload {
|
|
59
53
|
border-style: dashed;
|
|
@@ -72,7 +66,7 @@
|
|
|
72
66
|
.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; }
|
|
73
67
|
|
|
74
68
|
/* ---- wave: audio waveform stand-in for voice rows ----
|
|
75
|
-
<span class="wave"></span> — sits in a .
|
|
69
|
+
<span class="wave"></span> — sits in a .words, fills the width */
|
|
76
70
|
.wave {
|
|
77
71
|
/* only WHOLE bars show (Lh 2026-09-23 — a clipped last bar read thinner): the columns are as many whole bar-widths as fit
|
|
78
72
|
(auto-fill), spread edge to edge; bars that do not fit go to rows of no height, clipped away */
|