@usableapp/cardds 0.7.106 → 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 +97 -0
- package/bin/cardds.mjs +5 -3
- package/cardds.css +1 -0
- package/css/actions.css +9 -9
- package/css/base.css +6 -9
- package/css/card.css +16 -186
- package/css/finish.css +4 -4
- package/css/journey.css +1 -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 +12 -139
- 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 +4 -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/IconBtn.d.ts +1 -1
- package/dist/actions/IconBtn.js +1 -1
- package/dist/cardds.css +461 -422
- 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 +2 -28
- package/dist/lists/Row.js +2 -17
- package/dist/media/MapArea.d.ts +2 -2
- package/dist/media/MapArea.js +1 -1
- package/dist/media/Tile.d.ts +3 -7
- package/dist/media/Tile.js +5 -5
- 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 +1 -1
- package/lib/mcp.mjs +1 -1
- package/lint/rules.mjs +14 -7
- 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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,103 @@
|
|
|
2
2
|
|
|
3
3
|
> For: a project that consumes `@usableapp/cardds` and its AI — what changed between versions, written as before → after, so a screen can be brought up to date without reading the source. Newest first. A consumer never edits cardds; if something here leaves you without a way to do what you did before, report the gap. Each release section after 0.7.97 carries one `Components:` line, written by `node scripts/components-changed.mjs <previous tag>`: the components the release changed, or `all` when a file changed that any component can read (a token, the build), or `none`; check only the screens that import those.
|
|
4
4
|
|
|
5
|
+
## 0.7.107 — 2026-10-21
|
|
6
|
+
|
|
7
|
+
Every outer holds the same rows (Lh 2026-10-21): a `Card`, a `Sheet`, a stack sheet and a `Modal` are the paper and its behaviour, and inside each stand a `Head`, a `Body` and a `Foot`; a line is `Lead` · `Words` · `End`, the parts a `Row` and a `Card row` are both made of. Eleven parts and seven `Card` props are gone with no alias; `npx cardds codemod-parts <files>` moves a screen. Also: `Stat`, `StatRow`, `Delta` and `Tile stat` removed (a number is a `NumberBand` or a `KvCell`); the gallery's nav is one grouping by `src/` folder in a derived order; a scoped playwright run takes ports of its own.
|
|
8
|
+
|
|
9
|
+
Components: all (`css/tokens.css` lost `.delta` from its surface list; a new css file, `css/parts.css`, holds the parts and every outer's rule over them; any screen that imported a retired name fails the typecheck, see the table)
|
|
10
|
+
|
|
11
|
+
**Moving a screen.** Run the first row; it rewrites the rest. `--check` exits 1 naming each tag, prop, import or story it would still rewrite. It reads the TypeScript compiler from the project.
|
|
12
|
+
|
|
13
|
+
| before | after |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| a screen on the retired names | `npx cardds codemod-parts <files…>` (`--check` to list what is left) |
|
|
16
|
+
| `CardHead` · `SheetHead` · `Card heading caption end level` | `<Head><Words title meta level />{end}</Head>`, the first child (`SheetHead` → `Head centre`) |
|
|
17
|
+
| `SheetBody` · `ModalBody` | `<Body>` |
|
|
18
|
+
| `CardFoot` · `Card foot footLink footRound` | `<Foot link round>…</Foot>`, the last child |
|
|
19
|
+
| `RowLead` · `CardThumb` | `<Lead>` |
|
|
20
|
+
| `RowBody` · `CardBody` (in a `Card row`) | `<Words>` |
|
|
21
|
+
| `RowEnd` · `CardEnd` | `<End>` |
|
|
22
|
+
| `--card-head-*` · `--sheet-title-font` · `--sheet-sub-*` | `--head-*` · `--words-title-font` · `--words-meta-*` |
|
|
23
|
+
| `--card-foot-*` | `--foot-*` |
|
|
24
|
+
| `--row-lead-*` · `--card-thumb-*` · `--row-body-gap` · `--row-title-font` · `--row-meta-*` · `--row-end-gap` · `--card-end-gap` | `--lead-*` · `--words-gap` · `--words-title-font` · `--words-meta-*` · `--end-gap` |
|
|
25
|
+
| `.card__head` · `.sheet__head` · `.sheet__body` · `.modal__lift` · `.card__foot` · `.row__lead` · `.row__body` · `.row__end` | `.head` · `.body` · `.foot` · `.lead` · `.words` · `.end` |
|
|
26
|
+
|
|
27
|
+
The four entries on the parts under Changed below say each move in full: what it was, what governs it now, the stories that moved page, and the counts.
|
|
28
|
+
|
|
29
|
+
### Removed
|
|
30
|
+
- **`Stat`, `StatRow`, `Delta` and `Tile stat` are gone: a number is a `NumberBand` or a `KvCell`** (ruled Lh 2026-10-20, carried in 0.7.104 as "no new caller"; removed now). Before → after: a label, a big number and a caption stood in a `Stat` (a `StatRow` put a `Delta` "+7.5%" beside the label), and a value with its icon and label in a cell was a `Tile stat` → the number that is a card of its own is a `NumberBand` (`label` · `num` · `foot`); a number in a cell, alone or beside others, is a `KvCell` in a `KvGrid` (`icon` · `value` · `label`); a change beside it is words in the cell's label, not a pill. `Tile` loses `stat` and `value` (`.tile--stat`, `.tile__value`); `.stat`, `.stat--centre`, `.stat__row`, `.delta`, `.delta--neg` and the knobs `--stat-gap`, `--stat-row-gap`, `--tile-stat-gap`, `--tile-stat-min-h`, `--tile-value-font` and the eight `--delta-*` are gone (738 → 725 knobs). The root token `--neg-dark` stays (a theme may set it; nothing in cardds reads it now). A screen on the old names fails the typecheck (`Stat`, `StatRow`, `Delta` are not exported; `stat` and `value` are no props of `Tile`). Migration:
|
|
31
|
+
|
|
32
|
+
| before | after |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `<Stat><Text level="label" muted>Today</Text><Text level="stat">6.5 h</Text></Stat>` in a card or a sheet | `<KvGrid><KvCell value="6.5 h" label="Today" /></KvGrid>` |
|
|
35
|
+
| a `Stat` that was the whole card | `<NumberBand label={<>Hours<br />given</>} num="18" foot="…" />` |
|
|
36
|
+
| `<StatRow><Text level="label">Gross revenue</Text><Delta>+7.5%</Delta></StatRow>` | `label="Gross revenue · +7.5% vs last week"` on the `KvCell` |
|
|
37
|
+
| `<Tile stat icon="droplet" value="38 %" sub="Humidity" />` | `<KvCell icon="droplet" value="38 %" label="Humidity" />` |
|
|
38
|
+
| `Stat grow` (the room beside a `Ring`) · `Stat centre` (the one number centred under an avatar) | nothing on `KvCell` or `NumberBand` does this; a screen that needs it reports the gap |
|
|
39
|
+
|
|
40
|
+
Stories gone: `Stat/*` (four) and `Tile/StatOnEachTone` (the ink check reads `Kv/KvGridKami` on every tone). Migrated to a `KvCell`: `Avatar/WithIconXL`, `BtnGroup/Week`, `Home`'s wallet tabs, `Kv/Wallet`, `Ring/Default`, `Ring/InAHead`, `Tile/Dashboard`, `Track/OnADarkCard`.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- **One thing on a turning card's face may take the whole middle** (Lh 2026-10-21, the QR on timebank's request card: bigger, centred, the whole room). Before → after: the middle of a `Card back` face was a grid whose one row hugged its content, so a child given `height: 100%` (a QR with its caption, a code) stood at the top of an empty band, and nothing an app wrote could centre it or fill the room → the middle is a flex column of the same gap, so a child's `height: 100%` is the middle's height and it may centre or fill. Nothing for a consumer to change; a face whose middle is words stands as before (style snapshot: only the five `Card · Turns*` stories moved, and only by this). The story's back now draws the QR as a square as big as the room above its caption (`min(100%, 100cqh)` in a size container), centred. Components: Card. Stories `Card/Turns*`.
|
|
44
|
+
- **A line is made of one set of parts, `Lead` · `Words` · `End`, whether it is a `Row` or a `Card row`; the six parts each whole had of its own are gone** (Lh 2026-10-21, option A of three: the inner set is one, named without an outer prefix; the outer governs an inner's look only through the inner's knobs or a selector naming both, in the inner's file). Before → after: a row's parts were `RowLead`, `RowBody`, `RowEnd` (`.row__lead`, `.row__body` > `.row__title` · `.row__meta`, `.row__end`, lists.css) and a row card's were `CardThumb`, `CardBody`, `CardEnd` (`.card__thumb`, `.card__body`, `.card__end`, card.css), two names and two sets of knobs for one shape → the three parts live in `src/parts/` and draw `.lead`, `.words` > `.words__title` · `.words__meta`, `.end` (css/parts.css, after lists.css), the same props as before (`Lead icon`; `Words title meta`, and `level` reserved for a head's words; `End value caption`), and both wholes take them as direct children in that order: `<Row><Lead icon="gift" /><Words title="…" meta="…" /><End value="+1.0 h" caption="Tue" /></Row>`, `<Card row><Lead>01</Lead><Words><Text level="title">…</Text></Words><End>…</End></Card>`. A row card's lead keeps its 44 box in the card's ink and label type (`.card--row > .lead`, and a `FeedCard`'s icon lead the same), a row's its 48 box; the row's words keep `min-width: 0` and its end `flex: none` (`.row > .words`, `.row > .end`). No story's look moved: the style snapshot compares the computed look only now, so an element renamed (or a div that became a span) with the same values is no move, and a story shown on another page is named as a move of page. The catalog's `parts` is the folder rule, an export of `src/parts/`, and a part's `partOf` became `fits`, the wholes the stories stand it in, read from the previews (`cardds get Row` → `parts: Lead · Words · End`, `get Lead` → `fits: Card · Row`, `get End` → `Card · CardHead · Row`); `CardHead`, `CardFoot`, `SheetBody` are no parts until their PRs. The gallery has a `parts` group (End, Lead, Words, their row-card stories lifted from the Card page), and the `CardBody` page became `CardList` and `CardMedia`. Migration, run `npx cardds codemod-parts <files…>` on a screen (`--check` exits 1 naming what is left), or by hand:
|
|
45
|
+
|
|
46
|
+
| before | after |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `<RowLead icon="gift" />` | `<Lead icon="gift" />` |
|
|
49
|
+
| `<RowBody title="…" meta="…" />` · `<RowBody><Wave /></RowBody>` | `<Words title="…" meta="…" />` · `<Words><Wave /></Words>` |
|
|
50
|
+
| `<RowEnd value="+1.0 h" caption="Tue" />` | `<End value="+1.0 h" caption="Tue" />` |
|
|
51
|
+
| `<CardThumb>01</CardThumb>` in a `Card row` (or a `FeedCard`'s `icon`) | `<Lead>01</Lead>` |
|
|
52
|
+
| `<CardBody>…</CardBody>` in a `Card row` | `<Words>…</Words>` (a `CardBody` outside a `Card row` the codemod leaves and names: it was never one) |
|
|
53
|
+
| `<CardEnd>…</CardEnd>` in a row card or a head's `end` | `<End>…</End>` |
|
|
54
|
+
| `--row-lead-size` · `--row-lead-radius` · `--row-lead-border` | `--lead-size` · `--lead-radius` · `--lead-border` (and `--lead-font`, the row card's label type) |
|
|
55
|
+
| `--card-thumb-size` · `--card-thumb-radius` · `--card-thumb-font` | the `.card--row > .lead` defaults of `--lead-size` · `--lead-radius` · `--lead-font` (set the `--lead-*` knob on the list or the card) |
|
|
56
|
+
| `--row-body-gap` · `--row-title-font` | `--words-gap` · `--words-title-font` |
|
|
57
|
+
| `--row-meta-font` · `--row-meta-ink` · `--row-meta-gap` | `--words-meta-font` · `--words-meta-ink` · `--words-meta-gap` |
|
|
58
|
+
| `--row-end-gap` · `--card-end-gap` | `--end-gap` |
|
|
59
|
+
|
|
60
|
+
A screen on the old names fails the typecheck (the six are not exported). 725 → 722 knobs; 545 → 542 classes. Stories moved page, the same look: `Card/CardBodyTitleOnly` → `Words/WordsTitleOnly`, `Card/CardThumbInitialAndIcon` → `Lead/LeadInitialAndIcon`, `Card/CardEndInHead` → `End/EndInHead`, `CardListLinks` · `CardListPick` · `CardListWindow` → the `CardList` page, `CardMediaRated` · `CardMediaStrip` → the `CardMedia` page.
|
|
61
|
+
- **`Head` is the one head of a card, a sheet and a stack sheet** (Lh 2026-10-21, option A: every outer holds the same rows, a consumer changes the outer and keeps what is inside; the second of the inner parts). Before → after: a card's head was a `CardHead` (`heading` · `caption` · `end` · `level` · `top`, or children), written for you by `Card heading caption end level`, a stack sheet's head was a `CardHead` with children, and the stage sheet's was a `SheetHead` (`lead` · `title` · `sub` · `trail`), three heads for one row → one `Head` in `src/parts/` (`top`, `centre`, `long`, children): a lead (`Lead`, `StackBack`, `JourneyBadge`), the `Words` (`title`, `meta`, `level`), chips, an end (an `IconBtn`, a `Chip`, an `End`). `<Card foot={…}><Head><Words title="Sunday market" meta="Saturday" /><IconBtn sm icon="ellipsis" label="More" /></Head>…</Card>`. `Words` in a `Head` writes its title and its meta as the head's own children, flat: `h2.words__title.t-h2` (`h1` with `level="h1"`) then `span.words__meta.t-caption.t-muted`, so the caption still takes a row of its own under the title and the end. The stack's keyboard way in is kept: on the open sheet the title is a tab stop (`tabIndex` 0) and Enter or Space folds it; a title wider than its line still runs sideways once (`.is-long`, the Sheet's `text-indent`, the StackSheet's `translate`). `Card` no longer has `heading`, `caption`, `end`, `level`; it finds its `Head` child by type (a component of your own that wraps a `Head` is not found, as it was not with `CardHead`). `CardHead` and `SheetHead` are gone, no alias. `npx cardds codemod-parts <files…>` rewrites a screen (`--check` lists what still writes the old forms), and a story named `CardHeadX` or `SheetHeadX` is `HeadX`.
|
|
62
|
+
|
|
63
|
+
| before | after |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `<CardHead heading="Recent" caption={date} end={<Chip on>3 new</Chip>} />` | `<Head><Words title="Recent" meta={date} /><Chip on>3 new</Chip></Head>` |
|
|
66
|
+
| `<CardHead level="h1" heading="Welcome" top />` | `<Head top><Words title="Welcome" level="h1" /></Head>` |
|
|
67
|
+
| `<CardHead><StackBack /><Text level="h2" caps>Shopping</Text><IconRow>…</IconRow></CardHead>` | `<Head>` and `</Head>` round the same children |
|
|
68
|
+
| `<Card heading="Alone" caption="…" end={<Chip>1</Chip>} foot={…}>` | `<Card foot={…}><Head><Words title="Alone" meta="…" /><Chip>1</Chip></Head>…</Card>` |
|
|
69
|
+
| `<SheetHead lead={a} title="Create a room" sub="Settings" trail={b} />` | `<Head centre>{a}<Words title="Create a room" meta="Settings" />{b}</Head>` |
|
|
70
|
+
| `--card-head-col-gap` · `--card-head-row-gap` · `--card-head-lead-gap` · `--card-head-title-lines` | `--head-col-gap` · `--head-row-gap` · `--head-lead-gap` · `--head-title-lines` |
|
|
71
|
+
| `--sheet-title-font` · `--sheet-sub-font` · `--sheet-sub-ink` | `--words-title-font` · `--words-meta-font` · `--words-meta-ink` |
|
|
72
|
+
| `--card-head-min-h` · `--card-head-wrap` (a sheet set them on its head) | the sheet still sets them, now private to `parts.css` (`--head-min-h`, `--head-wrap`) |
|
|
73
|
+
| `.card__head`, `.card__head--top`, `.sheet__head`, `.sheet__title`, `.sheet__sub`, `.feed-card__head`, `.stack-sheet__title` | `.head`, `.head--top`, `.head--centre`, `.words__title`, `.words__meta`, `.feed-card > .head`, `.stack-sheet > .head > .words__title` |
|
|
74
|
+
|
|
75
|
+
The title's type is one knob, `--words-title-font`, which a head reads (default the level's own face) and three outers set: the stack for its one-line peek (`--fs-h1` over `--lh-solid`, or `--type-display`), a `Page` for its one `h1` (the card heading's size), and a theme anywhere; a sheet's title is the default, so a theme that set `--sheet-title-font` sets `--words-title-font` on the sheet. Every rule that names a head moved into `css/parts.css` under its outer (`.sheet-stack > .card > .head`, `.sheet > .head`, `.stack-sheet > .head`, `.card--bands > .head`, `.feed-card > .head`), so the rule-15 gate lists no override. The stack sheet's title keeps the overflow it computed (`hidden`, from the head's three-line clamp, which outranked the `overflow-x: clip` of `.stack-sheet__title` on every sheet); those two dead declarations are gone. 722 → 717 knobs and 542 → 538 classes, both re-pinned. Gallery: the pages `CardHead`, `SheetHead` and `StackHead` are one `Head` page in `parts` (the page says it fits a Card, a Sheet, a SheetStack, a Modal and a Page; `cardds get Head` prints the elements the stories stand it in directly, `fits: Card · Page · Sheet` once a modal's head stands in its Body, and a whole's `parts` list its rows first, then a line's parts); stories `CardHeadCustom`, `CardHeadTop` are `HeadCustom`, `HeadTop` (Card), `SheetHeadLeadTrail`, `SheetHeadTitleOnly` are `HeadLeadTrail`, `HeadTitleOnly` (Sheet), `LongTitle` and `StackBackRevealed` and `IconRowInRow` stand on the page unchanged, and `Picker · Words` is `Picker · WordValues` (a story named `Words` shadowed the part).
|
|
76
|
+
|
|
77
|
+
The style snapshot of the 499 stories: no element of a card, a sheet or a stack head moved (a box-for-box comparison of every element's x, y, width and height names none, and the snapshot reports no style). Six stories stand on another page with the same look (`CardHead · CardHeadCustom`, `CardHeadTop`, `IconRowInRow` and `SheetHead · LongTitle`, `StackHead · StackBackRevealed` are on `Head`; `Picker · Words` is `WordValues`). Three stories changed shape with the stage sheet's head, whose title and sub were wrapped in a `div` that `Head` does not draw: `SheetStage · Map` 36 → 35 elements, `SheetHead · SheetHeadLeadTrail` and `SheetHeadTitleOnly` are `Head · HeadLeadTrail` and `HeadTitleOnly`. In them the head is the same height (60.5 with a sub, 48 without), the buttons and the title's text do not move; the title's own box is 4.4px taller (the head's room for Thai marks, `padding-block-start` 0.2em given back by a negative margin), the sub's box is the column wide and as tall as the body's line (219 × 33 at y 484.5, against 55 × 22 at y 491.5) with its text centred on the same middle, about 1.5px higher.
|
|
78
|
+
- **`Body` is the one content row of a sheet, a stack sheet and a modal** (Lh 2026-10-21, option A: every outer holds the same rows; the third of the inner parts). Before → after: a sheet's content stood in a `SheetBody` (`.sheet__body`, the scroller) and a modal's in a `ModalBody` (`.modal__lift`), two pieces with the same job → one `Body` (`.body`, `src/parts/Body.tsx`): a grid column from the top that scrolls, governed by each outer in `css/parts.css` with the outer in the selector (`.sheet > .body` stops above the foot's strip and keeps a focused control above the cut, `.sheet-stack > .card > .body` follows the pull to refresh, `.modal > .body` is padded and shrinks under the modal's height). A `Modal` with no `Body` is still the confirm; `ModalAsk` stays one piece. Run `npx cardds codemod-parts <files>`: it renames both tags, folds their imports into one `Body`, and renames a story named `SheetBody…`.
|
|
79
|
+
|
|
80
|
+
| before | after |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `<Sheet><Head>…</Head><SheetBody>…</SheetBody></Sheet>` | `<Sheet><Head>…</Head><Body>…</Body></Sheet>` |
|
|
83
|
+
| `<Modal open><ModalBody>…</ModalBody><ModalAsk …/></Modal>` | `<Modal open><Body>…</Body><ModalAsk …/></Modal>` |
|
|
84
|
+
| `.sheet__body`, `.modal__lift` (a theme or a test that named them) | `.body`; in one outer only: `.sheet > .body`, `.modal > .body` |
|
|
85
|
+
| (none) | `--body-gap` (the card's gap) · `--body-pad-top` (none; a paper sheet's `--sp-6`) |
|
|
86
|
+
|
|
87
|
+
A screen on the old names fails the typecheck. `cardds check` now names a control written straight in a `Sheet` or a `Modal`, outside its `Body` (`field-off-sheet`: a sheet's children are its rows); a control in its `Head` (a `Dropdown` at the head's end) stays legal, and a `Body` in a `Card` is the card's (`field-in-card`). `cardds get Sheet` lists `parts: Head · Body` and `get Modal` `Body`; `get Body` prints `fits: Modal · Sheet` and `get Head` `Card · Page · Sheet` (a modal's head stands in its Body now), the elements the stories stand each in directly, so a stack sheet's parts are its `Card`'s and `get SheetStack` lists none. 717 → 719 knobs; 538 → 537 classes (the two retired become one). The style snapshot of the 499 stories names no story that moved; one stands on another page with the same look, `SheetBody · SheetBodyScrolls` → `Body · BodyScrolls`.
|
|
88
|
+
- **`Foot` is the one foot of a card, a sheet and a stack sheet, and `Card` has no foot props** (Lh 2026-10-21, option A: every outer holds the same rows; the fourth of the inner parts). Before → after: a card's buttons stood in a `CardFoot` (`.card__foot`, `link`, `round`), or `Card foot footLink footRound` wrote one for you as its last child → one `Foot` (`.foot`, `src/parts/Foot.tsx`, the same `link` and `round`), written as the card's or the sheet's last child; `Card`'s `foot`, `footLink` and `footRound` are gone. A turning card finds its `Foot` child by type and draws it on both faces, as it did its `CardFoot`. Each outer governs the foot in `css/parts.css` under its name: `.sheet > .foot` (the layer that stands on the box's bottom edge at every state), `.sheet-stack > .card.sheet--paper > .foot` (the paper band), and `.card--bands` (one rule with the head's band now, so the twin allowance went). The data props of the templates stay: `StackSheet foot`, `Home sheets[].foot`, `CallSheet` `foot` and `footLink`, `FeedCard foot` render a `Foot`. Run `npx cardds codemod-parts <files>`: it renames `CardFoot`, writes a `Card`'s foot props as a `<Foot>` last child (a fragment's children unwrapped), drops a `footRound` that drew no foot, and renames a `CardFoot…` story.
|
|
89
|
+
|
|
90
|
+
| before | after |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `<CardFoot><Btn>Later</Btn><Btn primary>Join</Btn></CardFoot>` | `<Foot><Btn>Later</Btn><Btn primary>Join</Btn></Foot>` |
|
|
93
|
+
| `<Card foot={<Btn primary>Offer help</Btn>} footLink={<Link sm>see all</Link>}>…</Card>` | `<Card>…<Foot link={<Link sm>see all</Link>}><Btn primary>Offer help</Btn></Foot></Card>` |
|
|
94
|
+
| `<Card footRound foot={<><IconBtn size="xl" …/><IconBtn size="xl" …/></>}>` | `<Card>…<Foot round><IconBtn size="xl" …/><IconBtn size="xl" …/></Foot></Card>` |
|
|
95
|
+
| `--card-foot-gap` · `--card-foot-ink` · `--card-foot-link-gap` | `--foot-gap` · `--foot-ink` · `--foot-link-gap` |
|
|
96
|
+
| `.card__foot`, `.card__foot--link`, `.card__foot--round`, `.card__foot-link` | `.foot`, `.foot--link`, `.foot--round`, `.foot__link` |
|
|
97
|
+
|
|
98
|
+
A screen on the old names fails the typecheck. `cardds get Card` lists `parts: Head · Foot · Lead · Words · End` and `get Sheet` `Head · Body · Foot`; `get Foot` prints `fits: Card · Sheet` (a stack sheet's foot stands in its `Card`). A `Words` in a stack sheet's head now calls a consumer's own `onKeyDown` before the fold. 719 knobs (three renamed). The style snapshot of the 499 stories names no story that moved; four stand on another page with the same look, `CardFoot · CardFootTwo`, `CardFootRound`, `CardFootWordsWide`, `CardFootFourAnswers` → `Foot · FootTwo`, `FootRound`, `FootWordsWide`, `FootFourAnswers`; one story is renamed with the same look, `IconBtnGroup · InACardFoot` → `IconBtnGroup · InAFoot`.
|
|
99
|
+
- **The migrated stories' numbers are a cell's size.** Before → after: the wallet's balance was `Text stat xl` and the others `Text stat`, each with its label above → a `KvCell` value (`--kv-cell-val-font`, h2) with its label under it. The style snapshot names the seven stories above as moved and nothing else (501 → 496 stories).
|
|
100
|
+
- **The gallery's nav: one grouping, by `src/` folder, in a derived order** (Lh 2026-10-21, chosen from a picture of three options). Before → after: the nav had three groupings behind a `catalog · frame · role` switch (kept in `localStorage`), a hand-written `NAV` of 18 groups and 107 names, and inside every group the pages stood in the `NAV` order, so the folder view interleaved a folder's pages by another grouping's index (scaffold read ActionBar, LayerHistory, TopBar, AppBar, Page, Pager, …) → the switch is gone and the nav draws Foundations, then one group a `src/` folder in alphabetical order, then `screens` last (Splash, Walkthrough, Home, Install, SignIn, SignUp, Requests in the app's order of first use, the one list left in `gallery/src/entries.ts`, then PostScreens, StampScreens, StickyScreens, StickyWelcome). Inside a folder the order is derived (`gallery/src/rank.mjs`): a part follows its whole (Card, then CardBody, CardFeed, CardFoot, CardHead; SheetStack, then StackHead, before SheetStage), everything else alphabetical. `Screen` (src/scaffold) has a page of its own under `scaffold`, so its three stories (Scrolling, Fill, Lead) are shown; its four palette stories are gone (the Foundations panel shows every palette, and a palette nested in a story failed the ink gate in dark mode); `Palette` is a link under Foundations, where its tokens are read; the `not a component` group is gone. The search matches the folder name where it matched the `NAV` group. No story's style moved (the style snapshot of 0.7.106 holds; the three Screen stories are added).
|
|
101
|
+
|
|
5
102
|
## 0.7.106 — 2026-10-21
|
|
6
103
|
|
|
7
104
|
The map edge to edge under a step's controls (`MapArea bleed`, and a step whose bleeding child reaches the sheet's edges); `Chip soft`; a card's head led by its date; `Row` in parts (`RowLead`, `RowBody`, `RowEnd`, and the props that drew them are gone); a turning two-tone card no longer cut at its edges; the catalog names a whole's parts.
|
package/bin/cardds.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// `npx cardds init` — set a project up the ONE way (Lh 2026-09-29): the project's theme.css, the check script, the ESLint rules.
|
|
3
3
|
// `npx cardds check [dir…]` — the rules of lint/rules.mjs over the project's source; exit 1 on an error.
|
|
4
4
|
// `npx cardds guide` — the few rules no checker sees, as text (the MCP server hands the same text over as its instructions).
|
|
5
|
+
// `npx cardds codemod-parts [--check] <files…>` — rewrite a screen from the retired parts (CardHead, SheetBody, CardFoot…) to Head · Body · Foot · Lead · Words · End.
|
|
5
6
|
// `npx cardds list | get <Name> | find [words…] [--represents a,b] [--shape s] [--interact k|none] | scaffold [id]` — the component catalog (lib/catalog.mjs) for an AI session; `npx cardds mcp` serves the same over MCP.
|
|
6
7
|
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
7
8
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
@@ -107,7 +108,7 @@ const asText = {
|
|
|
107
108
|
...(c.composes.length ? [`composes: ${c.composes.join(' · ')}`] : []),
|
|
108
109
|
...(c.composedBy.length ? [`composed by: ${c.composedBy.join(' · ')}`] : []),
|
|
109
110
|
...(c.parts?.length ? [`parts: ${c.parts.join(' · ')} — its children, each its own export`] : []),
|
|
110
|
-
...(c.
|
|
111
|
+
...(c.fits?.length ? [`fits: ${c.fits.join(' · ')} — the wholes it is a child of`] : []),
|
|
111
112
|
...(c.params ? ['\nparameters', ...(c.params.length ? [] : [' (none)']), ...c.params.map((p) => ` ${p.name}${p.optional && !p.default ? '?' : ''}: ${p.type}${p.default ? ` = ${p.default}` : ''}`)] : [
|
|
112
113
|
'\nprops',
|
|
113
114
|
...c.props.map((p) => ` ${p.name}${p.optional ? '?' : ''}: ${p.type}${p.slot ? ' [slot]' : ''}${p.description ? ` — ${one(p.description)}` : ''}`)]),
|
|
@@ -144,6 +145,7 @@ function ask(cmd, argv) {
|
|
|
144
145
|
return 0;
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
const
|
|
148
|
-
|
|
148
|
+
const codemod = (a) => import('../scripts/codemod-parts.mjs').then((m) => m.main(a), (e) => { console.error(`cardds codemod-parts: ${e.code === 'ERR_MODULE_NOT_FOUND' ? 'it reads the TypeScript compiler: install `typescript` in the project' : e.message}`); return 2; });
|
|
149
|
+
const code = cmd === 'check' ? check(args) : cmd === 'codemod-parts' ? await codemod(args) : cmd === 'init' ? init() : cmd === 'guide' ? (process.stdout.write(GUIDE), 0) : ['list', 'get', 'find', 'scaffold', 'mcp'].includes(cmd) ? await ask(cmd, args)
|
|
150
|
+
: (console.log('usage: cardds init | guide | check [dir…] | codemod-parts [--check] <files…> | list | get <Name> [--html] | find [words…] [--represents a,b] [--shape s] [--interact k|none] | scaffold [id] [--html [--css <href>]] | mcp (list get find scaffold mcp: --root <path> --json)'), cmd ? 2 : 0);
|
|
149
151
|
process.exitCode = code; // not process.exit: a big answer piped to a reader is cut off by an exit before it is flushed
|
package/cardds.css
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
@import url("css/people.css"); /* avatar, avatar stack, chooser row */
|
|
15
15
|
@import url("css/choice.css"); /* check, toggle, chip grid, days, calendar, mood, pin, composer */
|
|
16
16
|
@import url("css/lists.css"); /* row, rows, key-value, kv grid, timeline, legend, link */
|
|
17
|
+
@import url("css/parts.css"); /* the parts of a line: lead, words, end — a Row's and a row card's children */
|
|
17
18
|
@import url("css/sticky.css"); /* sticky note, its field, tone chooser, thread of posts, pile */
|
|
18
19
|
@import url("css/stamp.css"); /* stamp, empty place, album page, a stamp stuck on a surface */
|
|
19
20
|
@import url("css/post.css"); /* the post: postmark, a postcard read (letter), written (desk), kept (box) */
|
package/css/actions.css
CHANGED
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
/* a button may carry a WORD beside (or instead of) its icon — two named sides, ชาย · หญิง (Lh 2026-10-02): the label's look, one line */
|
|
107
107
|
.icon-btn-group > .icon-btn { display: flex; align-items: center; justify-content: center; gap: var(--sp-2); padding-inline: var(--sp-2); font: var(--type-label); font-size: var(--fs-sm); white-space: nowrap; }
|
|
108
108
|
.icon-btn-group > .icon-btn + .icon-btn { border-inline-start: var(--border-w) solid var(--icon-btn-group-rule, var(--icon-btn-group-border, var(--outline))); }
|
|
109
|
-
.
|
|
109
|
+
.foot > .icon-btn-group { flex: 1 1 0; }
|
|
110
110
|
/* --vertical: the buttons one over the other, ONE button wide — at a row's end, where a panel across would take the words' room */
|
|
111
111
|
.icon-btn-group--vertical { flex-direction: column; flex: none; width: var(--tap); }
|
|
112
112
|
.icon-btn-group--vertical > .icon-btn { flex: 1 0 auto; min-height: var(--tap); }
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
does not stretch it: a foot's `flex: 1 1 0` is undone for it below, as the foot undoes it for `.btn--form` (card.css). */
|
|
117
117
|
.icon-btn-group--form { flex: 0 1 auto; justify-self: center; margin-inline: auto; } /* no align-self: a sheet's foot stands its buttons on the bottom edge by `align-items: end` */
|
|
118
118
|
.icon-btn-group--form > .icon-btn { flex: 0 1 auto; width: auto; min-width: var(--icon-btn-group-form-min-w, var(--tap)); }
|
|
119
|
-
.
|
|
119
|
+
.foot > :is(.icon-btn-group--form, .icon-btn-line) { flex: 0 1 auto; }
|
|
120
120
|
/* --labelled: the label's words, then the panel, ONE line in the middle (the words are read, not pressed: no frame, plain ink); too narrow → the words wrap
|
|
121
121
|
above the panel, both centred */
|
|
122
122
|
.icon-btn-line { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--icon-btn-group-gap, var(--sp-3)); min-width: 0; justify-self: center; margin-inline: auto; }
|
|
@@ -367,7 +367,7 @@
|
|
|
367
367
|
|
|
368
368
|
|
|
369
369
|
/* ---- action note (Lh 2026-09-24): the main button's place, the main button's size, but ON the ground — no fill, no border, no
|
|
370
|
-
rounding — one line of muted words, an inline icon allowed; nothing to press. In a
|
|
370
|
+
rounding — one line of muted words, an inline icon allowed; nothing to press. In a Foot or an ActionBar row it takes the
|
|
371
371
|
button's share (the whole width), so a foot or a bar keeps its height whether it holds a button or the note. ---- */
|
|
372
372
|
.action-note {
|
|
373
373
|
flex: 1 1 0;
|
|
@@ -437,7 +437,7 @@
|
|
|
437
437
|
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
438
438
|
a.avatar, button.avatar, .menu__item, button.snackbar__body, .stamp--tap, button.stamp-cell, button.stamp-place, .post-letter__turn, .post-desk__slot, .post-desk__pick, .post-desk__reach, .stamp-on__remove):not(.link):is(:disabled, [aria-disabled="true"]),
|
|
439
439
|
:where(.chip--pick, .tile--pick, .mood__opt, .toggle-field, label.btn):has(:disabled) { opacity: var(--off); }
|
|
440
|
-
.sheet-stack--tap > .card > .
|
|
440
|
+
.sheet-stack--tap > .card > .head > :is(h1, h2, h3):focus-visible { outline: var(--focus); outline-offset: var(--sp-1); } /* the keyboard's way into a stack sheet */
|
|
441
441
|
@media (prefers-reduced-motion: reduce) { :root { --press-scale: 1; } } /* the give goes; the dim, the shade and the focus ring stay */
|
|
442
442
|
|
|
443
443
|
/* ==== the chip a choice draws: picked (choice.css drives it with its input), and --sign (#62: a block is written in its own file) ==== */
|
|
@@ -459,14 +459,14 @@
|
|
|
459
459
|
|
|
460
460
|
/* ---- where it sits in what card.css draws (docs/reference.md rule 15) ---- */
|
|
461
461
|
/* a head's ChipRow takes a line of its own under the title: at most two lines of chips, a chip's words cut with an ellipsis */
|
|
462
|
-
.
|
|
462
|
+
.head > .chip-row { flex: 1 1 100%; order: 2; max-height: calc(2 * (var(--fs-sm) * var(--chip-sm-line, var(--lh-body)) + 2 * var(--chip-sm-pad-y, var(--sp-0)) + 2 * var(--border-w)) + var(--sp-2)); overflow: hidden; }
|
|
463
463
|
/* …beside a date that leads the head (card.css) they stand in the date's row, every chip in sight: the meeting place may take a line of its own */
|
|
464
|
-
.
|
|
465
|
-
.
|
|
466
|
-
.
|
|
464
|
+
.head:has(> .tile-badge:first-child + .chip-row) > .chip-row { grid-column: 2; grid-row: 1; max-height: none; }
|
|
465
|
+
.head > .chip-row > .chip { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
466
|
+
.foot > .btn { flex: 1 1 0; min-width: 0; }
|
|
467
467
|
/* …unless the button says its own size: `Btn form` is as wide as its words in a foot too, centred (Lh 2026-10-10: one button filling
|
|
468
468
|
a card's foot read too big — which button a card wears is the project's choice, so the foot must not undo it) */
|
|
469
|
-
.
|
|
469
|
+
.foot > .btn--form { flex: 0 1 auto; }
|
|
470
470
|
/* the page dots under the card sit centred under it — the indicator belongs
|
|
471
471
|
with the page that changes, never in the bar (Lh, 2026-09-12) */
|
|
472
472
|
.screen__centre > .pager__at { justify-self: center; }
|
package/css/base.css
CHANGED
|
@@ -133,8 +133,8 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
133
133
|
|
|
134
134
|
/* ---- typography levels ---- */
|
|
135
135
|
/* --fs-self: each level publishes its own size, for a consumer that scales a level without knowing which */
|
|
136
|
-
.t-display { font: var(--type-display); letter-spacing: var(--track-display); margin: 0; text-wrap: balance; --fs-self: var(--fs-display); }
|
|
137
|
-
.t-h1 { font: var(--type-h1); letter-spacing: var(--track-h1); margin: 0; --fs-self: var(--fs-h1); }
|
|
136
|
+
.t-display { --_type: var(--type-display); font: var(--type-display); letter-spacing: var(--track-display); margin: 0; text-wrap: balance; --fs-self: var(--fs-display); }
|
|
137
|
+
.t-h1 { --_type: var(--type-h1); font: var(--type-h1); letter-spacing: var(--track-h1); margin: 0; --fs-self: var(--fs-h1); }
|
|
138
138
|
.t-h2 { font: var(--type-h2); letter-spacing: var(--track-h2); margin: 0; --fs-self: var(--fs-h2); }
|
|
139
139
|
.t-title { font: var(--type-title); margin: 0; --fs-self: var(--fs-title); }
|
|
140
140
|
.t-body { font: var(--type-body); margin: 0; }
|
|
@@ -188,9 +188,6 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
188
188
|
.page > .t-body { color: var(--page-body-ink, var(--on-surface)); max-width: 34ch; } /* full ink: a sentence is read, muted is for meta (Lh 2026-09-22 — the words on the base were grey and hard to read) */
|
|
189
189
|
/* an illustration among the words (Lh 2026-09-22): capped, so a tall picture never pushes the words under the bar;
|
|
190
190
|
(the walk's dots are NOT here — they are the Step's, after its Pages: step.css, Lh 2026-09-22) */
|
|
191
|
-
/* the page's ONE heading on the base (a screen with no TopBar: CardHead level="h1") reads at the card heading's size —
|
|
192
|
-
h1 is the semantic level, not a bigger face; --fs-h1 is the bar title's size (Lh 2026-09-22) */
|
|
193
|
-
.page > .card__head > .t-h1 { font-size: var(--fs-h2); }
|
|
194
191
|
.page .cta-pack {
|
|
195
192
|
display: flex;
|
|
196
193
|
align-items: start; /* buttons keep their own height, never the row's */
|
|
@@ -283,12 +280,12 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
283
280
|
|
|
284
281
|
/* ---- where it sits in what card.css draws (docs/reference.md rule 15) ---- */
|
|
285
282
|
/* flat head markup (Lh 2026-09-16 — no <p>, no wrapper, only padding and gap tokens):
|
|
286
|
-
<div class="
|
|
283
|
+
<div class="head"><h2 class="words__title t-h2">…</h2><span class="words__meta t-caption t-muted">…</span><button class="icon-btn icon-btn--sm">…</button></div>
|
|
287
284
|
The heading and the far-right thing share the first row; a caption sits on
|
|
288
285
|
a row below (order 1), whatever the DOM order — so an author writes the
|
|
289
286
|
words in reading order. */
|
|
290
|
-
.
|
|
291
|
-
.
|
|
287
|
+
.head > :is(.t-caption, .t-body) { flex: 1 1 100%; order: 1; margin: 0; }
|
|
288
|
+
.head:has(> .stamp) > :is(.t-caption, .t-body) { grid-column: 1; grid-row: 2; align-self: start; }
|
|
292
289
|
/* ---- where it sits in what stack.css draws (docs/reference.md rule 15) ---- */
|
|
293
290
|
.sheet-stack__base > .screen__centre { padding-inline: 0; }
|
|
294
291
|
/* ---- where it sits in what sheet.css draws (docs/reference.md rule 15) ---- */
|
|
@@ -326,7 +323,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
|
326
323
|
the lead + the picture's own height (--page-media-h), square — so the words start where they always did */
|
|
327
324
|
.step > .pages > .page:has(> :is(img, .ph):first-child) { margin-top: 0; }
|
|
328
325
|
.step > .empty-state { flex: none; }
|
|
329
|
-
.
|
|
326
|
+
.body > .empty-state:only-child { flex: none; }
|
|
330
327
|
/* ---- where it sits in what actions.css draws (docs/reference.md rule 15) ---- */
|
|
331
328
|
/* ---- top bar: back + screen title ---- */
|
|
332
329
|
.topbar .t-h1 {
|
package/css/card.css
CHANGED
|
@@ -42,80 +42,14 @@
|
|
|
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
|
-
/* a head that LEADS with a date (Lh 2026-10-21, timebank's request card): a `CardHead` whose first child is a JourneyBadge and whose second is a
|
|
104
|
-
ChipRow. The date stands at the left and the chips beside it, the two as tall as each other; the heading and its caption take the whole
|
|
105
|
-
width under them, so the heading sits right over the words of the middle. The date is always as tall as the chips. The gap under the date row is --card-head-lead-gap; the date and the chips place themselves (journey.css, actions.css).
|
|
106
|
-
<div class="card__head"><span class="tile-badge tile-badge--stretch">…</span><div class="chip-row">…</div><div><h2 class="t-h1">…</h2>…</div></div> */
|
|
107
|
-
.card__head:has(> .tile-badge:first-child + .chip-row) {
|
|
108
|
-
display: grid;
|
|
109
|
-
grid-template-columns: auto minmax(0, 1fr);
|
|
110
|
-
align-items: center;
|
|
111
|
-
row-gap: var(--card-head-lead-gap, var(--sp-3));
|
|
112
|
-
}
|
|
113
|
-
.card__head:has(> .tile-badge:first-child + .chip-row) > :nth-child(n + 3) { grid-column: 1 / -1; }
|
|
114
|
-
|
|
115
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
|
|
116
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,
|
|
117
51
|
so a theme that sets that knob still wins. */
|
|
118
|
-
:is(.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 */
|
|
119
53
|
|
|
120
54
|
/* ---- paper bands: the two-tone card (`Card paper="bands"`, Lh 2026-10-19): the head and the foot are paper bands edge to
|
|
121
55
|
edge over the card's padding, no hairline; the middle between them keeps the card's tone, lightened (--card-middle-bg). The card
|
|
@@ -127,20 +61,6 @@
|
|
|
127
61
|
overflow: hidden;
|
|
128
62
|
}
|
|
129
63
|
.card--bands.card--turns { background: none; overflow: visible; } /* the turning box keeps --dark: the faces' ink is computed from it */
|
|
130
|
-
:is(.card--bands, .card--bands > .card__face) > :is(.card__head, .card__foot) {
|
|
131
|
-
--card-bg: var(--card-1-bg);
|
|
132
|
-
--dark: var(--card-1-dark);
|
|
133
|
-
background: var(--card-bg);
|
|
134
|
-
color: var(--card-ink);
|
|
135
|
-
margin-inline: calc(-1 * var(--card-pad));
|
|
136
|
-
padding-inline: var(--card-pad);
|
|
137
|
-
}
|
|
138
|
-
:is(.card--bands, .card--bands > .card__face) > .card__head { margin-top: calc(-1 * var(--card-pad)); padding-block: var(--card-pad); }
|
|
139
|
-
:is(.card--bands, .card--bands > .card__face) > .card__head { padding-block-end: var(--card-band-seam, var(--sp-2)); }
|
|
140
|
-
/* the seam: the heading and the words under it read as ONE paragraph that changes colour at the band's edge (Lh 2026-10-21), so the room on
|
|
141
|
-
each side of the head band's lower edge is --card-band-seam, not the card's padding and gap */
|
|
142
|
-
:is(.card--bands, .card--bands > .card__face) > .card__head + * { margin-block-start: calc(var(--card-band-seam, var(--sp-2)) - var(--card-gap)); }
|
|
143
|
-
: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 */
|
|
144
64
|
|
|
145
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;
|
|
146
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
|
|
@@ -162,10 +82,10 @@
|
|
|
162
82
|
position: relative;
|
|
163
83
|
}
|
|
164
84
|
.card__face > * { min-width: 0; }
|
|
165
|
-
.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) */
|
|
166
86
|
.card__turn { position: absolute; inset: 0; width: var(--px); height: var(--px); padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); }
|
|
167
87
|
.card__face:has(> .card__turn:focus-visible) { outline: var(--focus); outline-offset: calc(-1 * var(--sp-1)); }
|
|
168
|
-
.
|
|
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; }
|
|
169
89
|
|
|
170
90
|
/* ---- bleed: a scrolling track that runs to the container's edges ----
|
|
171
91
|
For a horizontal strip inside a padded container — .avatar-pick, .chip-row,
|
|
@@ -205,19 +125,6 @@
|
|
|
205
125
|
is genuinely first or last needs the corner clipped for it */
|
|
206
126
|
.card:has(> :is(.bleed, .map--bleed):first-child), .card:has(> :is(.bleed, .map--bleed):last-child) { overflow: clip; }
|
|
207
127
|
|
|
208
|
-
/* foot: the card's action buttons on one line. One button takes the width,
|
|
209
|
-
up to four share it in even parts — so the gaps read even too — and from
|
|
210
|
-
three on they drop their side padding so the labels keep the room. */
|
|
211
|
-
.card__foot { display: flex; gap: var(--card-foot-gap, var(--sp-2)); align-items: center; color: var(--card-foot-ink, inherit); }
|
|
212
|
-
.card__foot:has(> :nth-child(3)) > .btn { --btn-pad-x: var(--sp-2); }
|
|
213
|
-
/* a small link under the buttons (CardFoot link — Lh 2026-09-23, as the ActionBar's ctaLink): the row wraps, the link takes a
|
|
214
|
-
whole short row of its own, centred — it never shares the buttons' line */
|
|
215
|
-
.card__foot--link { flex-wrap: wrap; align-content: end; row-gap: var(--card-foot-link-gap, var(--sp-1)); }
|
|
216
|
-
.card__foot-link { flex: 1 0 100%; display: flex; justify-content: center; }
|
|
217
|
-
/* 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
|
|
218
|
-
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 */
|
|
219
|
-
.card__foot--round { justify-content: center; gap: var(--card-foot-gap, var(--sp-6)); }
|
|
220
|
-
|
|
221
128
|
/* media placeholder for wireframes — currentColor so it survives any palette */
|
|
222
129
|
.ph {
|
|
223
130
|
background:
|
|
@@ -334,7 +241,6 @@
|
|
|
334
241
|
align-items: center;
|
|
335
242
|
gap: var(--card-row-gap, var(--sp-3));
|
|
336
243
|
}
|
|
337
|
-
.card--row .card__body { flex: 1; display: grid; gap: var(--sp-0); }
|
|
338
244
|
|
|
339
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
|
|
340
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
|
|
@@ -351,7 +257,7 @@
|
|
|
351
257
|
}
|
|
352
258
|
|
|
353
259
|
/* ---- the cross that takes a row off the list (`Card row onRemove`): small, at the row's top right corner, inside it ----
|
|
354
|
-
<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>
|
|
355
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
|
|
356
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
|
|
357
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. */
|
|
@@ -379,21 +285,10 @@
|
|
|
379
285
|
.card__remove > .icon { --icon-size: 62%; }
|
|
380
286
|
.card__remove:focus-visible { outline: var(--focus); outline-offset: var(--sp-0); }
|
|
381
287
|
|
|
382
|
-
/* leading thumb/icon in a row */
|
|
383
|
-
.card__thumb {
|
|
384
|
-
width: var(--card-thumb-size, var(--tap-sm)); height: var(--card-thumb-size, var(--tap-sm));
|
|
385
|
-
flex: none;
|
|
386
|
-
border-radius: var(--card-thumb-radius, var(--r-control));
|
|
387
|
-
border: var(--border-w) solid currentColor;
|
|
388
|
-
display: grid;
|
|
389
|
-
place-items: center;
|
|
390
|
-
font: var(--card-thumb-font, var(--type-label));
|
|
391
|
-
}
|
|
392
|
-
|
|
393
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) ----
|
|
394
289
|
<div class="card-feed">
|
|
395
|
-
<article class="card feed-card"><div class="
|
|
396
|
-
<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>
|
|
397
292
|
<article class="card feed-card feed-card--small" role="button" tabindex="0">… <div class="feed-card__more" inert>…</div></article>
|
|
398
293
|
<p class="t-caption card-feed__end" role="note">the end</p>
|
|
399
294
|
</div>
|
|
@@ -404,9 +299,7 @@
|
|
|
404
299
|
shut content in the scrollable overflow of whatever holds the feed — and hidden once the row has shut. */
|
|
405
300
|
.card-feed { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; gap: var(--card-feed-gap, var(--card-gap-rest)); }
|
|
406
301
|
.feed-card { row-gap: 0; }
|
|
407
|
-
.feed-
|
|
408
|
-
.feed-card__head:has(> :is(.card__thumb, .feed-card__lead)) { grid-template-columns: auto minmax(0, 1fr); }
|
|
409
|
-
.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; }
|
|
410
303
|
.feed-card__lead { display: grid; align-self: center; } /* a Stamp here is the small one, 48 × 60 (stamp.css) */
|
|
411
304
|
.card-feed__end { text-align: center; }
|
|
412
305
|
.feed-card__more { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr; }
|
|
@@ -423,22 +316,6 @@
|
|
|
423
316
|
.feed-card--small .feed-card__rest { transition: visibility 0s var(--motion-sheet); }
|
|
424
317
|
}
|
|
425
318
|
|
|
426
|
-
/* ---- inline stat block (revenue-style) ---- */
|
|
427
|
-
.stat { display: grid; gap: var(--stat-gap, var(--sp-1)); }
|
|
428
|
-
.stat--centre { text-align: center; justify-items: center; } /* the one number of a sheet or a stage, under a centred avatar */
|
|
429
|
-
.stat__row { display: flex; align-items: center; justify-content: space-between; gap: var(--stat-row-gap, var(--sp-3)); }
|
|
430
|
-
|
|
431
|
-
.delta {
|
|
432
|
-
font: var(--delta-font, var(--type-caption));
|
|
433
|
-
font-weight: var(--fw-bold);
|
|
434
|
-
padding: var(--delta-pad-y, var(--sp-1)) var(--delta-pad-x, var(--sp-2));
|
|
435
|
-
border-radius: var(--delta-radius, var(--r-chip));
|
|
436
|
-
background: var(--delta-bg, var(--card-3-bg));
|
|
437
|
-
--dark: var(--card-3-dark);
|
|
438
|
-
color: var(--delta-ink, var(--card-ink));
|
|
439
|
-
}
|
|
440
|
-
.delta--neg { background: var(--delta-neg-bg, var(--neg)); --dark: var(--neg-dark); color: var(--delta-neg-ink, var(--card-ink)); }
|
|
441
|
-
|
|
442
319
|
/* ---- panes: a few panes side by side inside a card or a sheet, one in sight (Lh 2026-10-02) ----
|
|
443
320
|
<div class="panes" role="group"><div class="panes__rail"><div class="panes__pane">…</div>…</div><span class="pager__at">…</span></div>
|
|
444
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
|
|
@@ -462,7 +339,7 @@
|
|
|
462
339
|
|
|
463
340
|
/* ---- cover: the image IS the card; text over a scrim ----
|
|
464
341
|
<article class="card card--cover"><div class="ph" role="img"></div>
|
|
465
|
-
<div class="
|
|
342
|
+
<div class="head"><button class="icon-btn icon-btn--sm">♥</button></div>
|
|
466
343
|
<div><h2 class="t-h1">Art Institute</h2><p class="t-body">111 South Michigan Ave</p></div>
|
|
467
344
|
<div class="row">…meta…</div></article> */
|
|
468
345
|
.card--cover {
|
|
@@ -552,14 +429,6 @@
|
|
|
552
429
|
.sheet-stack > .card:nth-child(3) { margin-top: calc(2 * var(--stack-step)); }
|
|
553
430
|
.sheet-stack > .card:nth-child(4) { margin-top: calc(3 * var(--stack-step)); }
|
|
554
431
|
.sheet-stack > .card:nth-child(5) { margin-top: calc(4 * var(--stack-step)); }
|
|
555
|
-
/* stack headers: uniform — [back?] title left, icons right, one height,
|
|
556
|
-
same gap to content on every card */
|
|
557
|
-
.sheet-stack > .card > .card__head {
|
|
558
|
-
align-items: center;
|
|
559
|
-
justify-content: flex-start;
|
|
560
|
-
min-height: var(--tap);
|
|
561
|
-
}
|
|
562
|
-
.sheet-stack--display > .card > .card__head { align-items: start; min-height: 0; }
|
|
563
432
|
/* ============================================================
|
|
564
433
|
.sheet-stack--tap — tap a peeked sheet open; a back button returns it.
|
|
565
434
|
|
|
@@ -612,8 +481,7 @@
|
|
|
612
481
|
overscroll-behavior: contain;
|
|
613
482
|
}
|
|
614
483
|
.sheet-stack--tap > .card.is-open.is-dragging { transition: none; }
|
|
615
|
-
.sheet-stack--tap > .card
|
|
616
|
-
.sheet-stack--tap > .card.is-open:not(:has(> .sheet__body)) {
|
|
484
|
+
.sheet-stack--tap > .card.is-open:not(:has(> .body)) {
|
|
617
485
|
padding-top: calc(var(--card-pad) + var(--_refresh, 0rem));
|
|
618
486
|
transition: translate var(--motion-sheet) var(--ease), padding-top var(--motion-sheet) var(--ease);
|
|
619
487
|
}
|
|
@@ -649,56 +517,18 @@
|
|
|
649
517
|
only, the sheet's size is untouched. A pull on the head keeps them square until the finger lets go. */
|
|
650
518
|
.sheet-stack--flood > .card { transition: translate var(--motion-sheet) var(--ease), border-radius var(--motion-sheet) var(--ease); }
|
|
651
519
|
.sheet-stack--flood > .card.is-open { border-top-left-radius: 0; border-top-right-radius: 0; }
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
transition: padding-left var(--motion-sheet) var(--ease);
|
|
656
|
-
}
|
|
657
|
-
/* ---- a stacked sheet with a SheetBody has the ONE sheet shape (Lh 2026-09-23, the home layout): sheet head
|
|
658
|
-
pinned at the top · sheet body scrolls, as long as it needs · sheet foot (a CardFoot, the CTA) at the bottom.
|
|
659
|
-
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
|
|
660
523
|
keeps the old way: the whole open card scrolls. ---- */
|
|
661
|
-
.sheet-stack--tap > .card:has(> .
|
|
662
|
-
.sheet-stack--tap > .card:has(> .
|
|
663
|
-
/* a paper stack's foot is paper to the card's edges, and it covers the tinted flex gap above it so the
|
|
664
|
-
button does not sit on a colour stripe. The head's gap stays: it is outside this foot. The button stays
|
|
665
|
-
where it was — the padding gives back what the bleed takes. */
|
|
666
|
-
.sheet-stack > .card.sheet--paper > .card__foot {
|
|
667
|
-
background: var(--action-bg);
|
|
668
|
-
color: var(--card-ink);
|
|
669
|
-
margin-top: calc(-1 * var(--card-gap));
|
|
670
|
-
margin-inline: calc(-1 * var(--card-pad));
|
|
671
|
-
margin-bottom: calc(-1 * var(--card-pad));
|
|
672
|
-
padding-top: var(--card-gap);
|
|
673
|
-
padding-inline: var(--card-pad);
|
|
674
|
-
padding-bottom: var(--card-pad);
|
|
675
|
-
}
|
|
676
|
-
.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; }
|
|
677
526
|
/* ---- the back button, SMALL (Lh 2026-09-23: "give the sheet head more room" — and then "a glyph alone does not read as
|
|
678
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
|
|
679
528
|
(was 56px). Its tap target stays 44px: an invisible ::after around the disc. ---- */
|
|
680
|
-
.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))); }
|
|
681
|
-
: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); }
|
|
682
|
-
.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))); }
|
|
683
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),
|
|
684
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) */
|
|
685
|
-
.sheet-stack--tap > .card
|
|
686
|
-
/* ---- where it sits in what sheet.css draws (docs/reference.md rule 15) ---- */
|
|
687
|
-
.sheet > .card__foot {
|
|
688
|
-
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 */
|
|
689
|
-
align-items: end;
|
|
690
|
-
align-content: end;
|
|
691
|
-
padding: 0 var(--card-pad) calc(var(--card-pad) + var(--_safe-b-open));
|
|
692
|
-
background: linear-gradient(var(--card-bg), var(--card-bg)) bottom / 100% var(--_foot) no-repeat;
|
|
693
|
-
pointer-events: none;
|
|
694
|
-
translate: 0 calc(-1 * var(--_y) - var(--_drag, 0rem));
|
|
695
|
-
transition: translate var(--motion-sheet) var(--ease), visibility 0s;
|
|
696
|
-
}
|
|
697
|
-
.sheet.is-dragging > .card__foot { transition: none; }
|
|
698
|
-
/* 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
|
|
699
|
-
LEAVES AND ARRIVES WITH ITS SHEET — solid the whole way, hidden only once the slide is over. It used to stay in place and
|
|
700
|
-
fade: the body slid through the strip behind a half-transparent button (timebank, Lh 2026-10-01: "ตัวหนังสือลอดใต้ปุ่ม"). */
|
|
701
|
-
: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; }
|
|
702
532
|
/* ---- where it sits in what step.css draws (docs/reference.md rule 15) ---- */
|
|
703
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
|
|
704
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
|
|
@@ -727,7 +557,7 @@
|
|
|
727
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; }
|
|
728
558
|
|
|
729
559
|
/* ---- where it sits in what stack.css draws: a sheet pulled to refresh holds still (docs/reference.md rule 15) ---- */
|
|
730
|
-
.sheet-stack--tap > .card.is-open.is-refreshing:not(:has(> .
|
|
560
|
+
.sheet-stack--tap > .card.is-open.is-refreshing:not(:has(> .body)) { transition: none; }
|
|
731
561
|
|
|
732
562
|
/* ---- where it sits in what step.css draws: the reading block, a walk of pages (docs/reference.md rule 15) ---- */
|
|
733
563
|
.step:has(> .sheet--peek) > .pages { padding-bottom: var(--sheet-peek); }
|