@usableapp/cardds 0.7.16 → 0.7.18
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 +32 -0
- package/README.md +23 -20
- package/cardds.css +3 -3
- package/css/actions.css +60 -38
- package/css/base.css +43 -17
- package/css/card.css +18 -13
- package/css/choice.css +21 -33
- package/css/forms.css +0 -3
- package/css/journey.css +0 -2
- package/css/layover.css +14 -24
- package/css/lists.css +1 -6
- package/css/media.css +7 -15
- package/css/numbers.css +5 -21
- package/css/people.css +1 -8
- package/css/sheet.css +12 -14
- package/css/stack.css +17 -21
- package/css/step.css +7 -8
- package/css/tokens.css +19 -4
- package/dist/actions/Btn.d.ts +1 -1
- package/dist/actions/Dropdown.js +1 -6
- package/dist/actions/Fab.d.ts +1 -1
- package/dist/actions/Fab.js +3 -2
- package/dist/cardds.css +230 -232
- package/dist/cards/Card.d.ts +23 -7
- package/dist/cards/Card.js +23 -8
- package/dist/cards/CardHead.d.ts +0 -1
- package/dist/cards/CardHead.js +1 -1
- package/dist/choice/Calendar.js +1 -2
- package/dist/choice/Mood.js +2 -2
- package/dist/forms/Field.js +2 -3
- package/dist/forms/FileBtn.js +1 -1
- package/dist/forms/Pin.js +6 -10
- package/dist/index.js +2 -2
- package/dist/layover/Banner.d.ts +10 -3
- package/dist/layover/Banner.js +6 -3
- package/dist/lists/Row.js +3 -3
- package/dist/media/Postcard.js +2 -2
- package/dist/numbers/Band.d.ts +3 -1
- package/dist/numbers/Picker.js +1 -7
- package/dist/scaffold/ActionBar.d.ts +3 -3
- package/dist/scaffold/ActionBar.js +2 -2
- package/dist/scaffold/Centre.d.ts +1 -1
- package/dist/scaffold/Centre.js +1 -1
- package/dist/scaffold/PagerAt.d.ts +1 -1
- package/dist/scaffold/PagerAt.js +1 -1
- package/dist/scaffold/Screen.js +2 -4
- package/dist/sheets/Drawer.js +3 -4
- package/dist/sheets/Sheet.js +8 -19
- package/dist/sheets/SheetStack.d.ts +2 -2
- package/dist/sheets/SheetStack.js +18 -10
- package/dist/sheets/SheetSteps.js +5 -7
- package/dist/sheets/StackBack.js +2 -1
- package/dist/sheets/StackSheet.d.ts +3 -2
- package/dist/sheets/StackSheet.js +6 -9
- package/dist/state.d.ts +19 -2
- package/dist/state.js +47 -7
- package/dist/templates/CallSheet.js +1 -2
- package/dist/templates/Home.d.ts +3 -2
- package/dist/templates/Home.js +1 -2
- package/dist/templates/Splash.js +2 -3
- package/dist/templates/Walkthrough.js +2 -2
- package/dist/type/Text.d.ts +1 -1
- package/package.json +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
> For: a project that consumes `@usableapp/cardds` and its AI — what changed between versions, written as before → after, so a screen can be brought up to date without reading the source. Newest first. A consumer never edits cardds; if something here leaves you without a way to do what you did before, report the gap.
|
|
4
4
|
|
|
5
|
+
## 0.7.18 — 2026-09-27
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- A lone `Page` in a `Step` on a fill `Screen` with a `bottom` now ends with the bottom layer's room (`--bottom-h`), as a walk's page always did. Before: its last words (a closing `Link`) lay under the `ActionBar` and could not be tapped (timebank onboard-04, 2026-09-24). Now: the page scrolls only when the layer would cover its words, just far enough to clear them; with no bottom nothing moves. Nothing to change in a screen.
|
|
9
|
+
|
|
10
|
+
## 0.7.17 — 2026-09-27
|
|
11
|
+
|
|
12
|
+
Second refactor pass (what the first skipped). Most of it is internal; these can touch a screen:
|
|
13
|
+
|
|
14
|
+
### Changed (may touch a screen)
|
|
15
|
+
- `Banner` is controlled like every disclosure: `open` + `onOpenChange`, or `defaultOpen`. Before: `open` was the native attribute. `<Banner open>` meaning "start open" → `defaultOpen`.
|
|
16
|
+
- A controlled component no longer asks for the value it already shows: tapping the picked day (`Calendar onSelect`), the picked dot (`Mood onChange`) calls nothing.
|
|
17
|
+
- `FileBtn`: its file input is visually hidden instead of `hidden` — Tab reaches it, Enter/Space opens the picker, the button shows the focus ring.
|
|
18
|
+
- `SheetStack` (tap): a head's title takes the keyboard's focus where a tap on it works (every title while none is open; the open one's while one is) — Enter/Space opens or folds.
|
|
19
|
+
- peerDependencies: React `>=19` (components take `ref` as a prop; React 18 never worked).
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- `Card` SLOTS (Lh 2026-09-27): `heading` · `caption` · `end` · `level` fill its head, `foot` · `footLink` its foot; the children are the content between — the card writes head first, foot last. `<CardHead>`/`<CardFoot>` as children still work (a head of its own shape); both at once warns. `<Card heading="…" foot={<Btn primary>Join</Btn>}>…</Card>` replaces a CardHead + CardFoot pair.
|
|
23
|
+
- Tap answer for every tappable thing (the PRESS block, actions.css): a finger-sized control GIVES (`--press-scale`); a card-wide one — a row as a link/label, a Row `more` line, a Banner line, a card as a link, a Toggle with words — DIMS (`--press-dim` 0.6); a peeked stack sheet and a Postcard SHADE (`--press-shade` 0.93, so nothing behind shows through). New in it: Check, Toggle, Mood, Sheet handle, Avatar as a link/button, the Slider's knob, SheetSteps' step bar. A disabled control has no pointer, no press, and fades to `--off` (0.4); a label whose input is disabled too.
|
|
24
|
+
- Tokens: `--press-dim`, `--press-shade`, `--off`, `--rest` (0.6, an unpicked choice), `--faint` (0.3, far dots, picker items), `--motion-quick` (0.2s: chevrons, the toggle, menus), `--z-bar` / `--z-fab` / `--z-top`.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- Enter in a SheetSteps field could open a `FileBtn`'s picker (a `label.btn` counted as the step's button).
|
|
28
|
+
- The Toggle's height and knob gap were raw rem — now on `--tap-base` (same at the default).
|
|
29
|
+
- The sheet's foot strip ignored the `--link-sm-min-h` knob; `.sheet-stage > .float-bar` defeated `--float-bar-pad-x`.
|
|
30
|
+
- The Banner opens in 0.2s (was 0.25s — the one value that moved).
|
|
31
|
+
|
|
32
|
+
### Internal
|
|
33
|
+
- `src/state.ts`: `useControlled(value, initial, onChange)` now returns `[shown, request]` — the no-change guard, the own copy and the ask in one stable function (12 copies gone; the Sheet no longer rebuilt its ResizeObserver every render); `useDisclosure` (Row `more`, Banner); `watchSize` (5 ResizeObserver copies); `useLatest`. `Tone` type exported once. StackBack, Fab and the StackSheet tabs render through `IconBtn`.
|
|
34
|
+
- CSS: `.card--3` muted words one rule; one zero-specificity button reset; one hidden-input rule; one scrollbar-hiding rule; dead `.dotgrid--stars` and float-bar padding rule gone; split rules merged; stale comments (cardds.js doing behaviour, bars outside the screen) rewritten.
|
|
35
|
+
- Tests: `tests/helpers.js` (shared rect/near/tok/settle/story…); PIN + Modal flakes fixed with the page clock and finished animations; the knob probe focuses its own field. Fixtures lost ~500 lines nothing asserted (`stack.html` gone). `scripts/catalog.mjs` lost the dropped MCP server's code; `validate.mjs` accepts `.js` imports and `ComponentProps`. New scripts: `typecheck`, `snapshot:write`, `snapshot:check`.
|
|
36
|
+
|
|
5
37
|
## 0.7.16 — 2026-09-25
|
|
6
38
|
|
|
7
39
|
Refactor + additions. Same props, same markup, same computed style on every story.
|
package/README.md
CHANGED
|
@@ -18,8 +18,8 @@ from props. The demo is the gallery (`npm run dev`):
|
|
|
18
18
|
every component's stories, live, searchable.
|
|
19
19
|
|
|
20
20
|
Repo: **https://github.com/everysundays/cardds** (private — this is the
|
|
21
|
-
source of truth; every consuming project
|
|
22
|
-
the other way round).
|
|
21
|
+
source of truth; every consuming project installs the npm package
|
|
22
|
+
`@usableapp/cardds`, never the other way round).
|
|
23
23
|
|
|
24
24
|
**Wireframe by default** — grayscale with visible outlines. Color is opt-in,
|
|
25
25
|
lives only in one file, applied with one attribute. Two faces ship with it
|
|
@@ -42,12 +42,12 @@ import '@usableapp/cardds/dist/cardds.css';
|
|
|
42
42
|
import './theme.css'; // the project's own look, after the system's — see "A project's theme"
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
Every behaviour is the component's own React code — the `Sheet`'s handle (tap, drag,
|
|
46
|
+
keyboard) with the focus and on-screen-keyboard raises, the `SheetStack` opening and
|
|
47
|
+
closing, the `Pin` auto-advance, the `Modal`'s leaving — each controlled like an input
|
|
48
|
+
(`x` + `onXChange`, or `defaultX`). `src/index.ts` imports `cardds.js` for the environment
|
|
49
|
+
only: the keyboard's height (`--kb`) and `.bleed`'s scrollbar. `Icon` inlines the 61
|
|
50
|
+
symbols of `icons.svg` at build time, so no sprite file ships.
|
|
51
51
|
|
|
52
52
|
**The gallery** (`gallery/`, `npm run dev` → http://localhost:5174): Vite + React Router, `cardds`
|
|
53
53
|
aliased to `src/`, so the gallery and the package share one truth with no build between them.
|
|
@@ -160,7 +160,7 @@ the card is the material, the sheet is what it is in the stack.
|
|
|
160
160
|
sixth would render at slot 0, on top of the first.
|
|
161
161
|
- **Every head needs its own `.sheet-stack__back`** if the stack is `--tap`. It stays
|
|
162
162
|
hidden until that sheet is open, then slides into a reserved header slot.
|
|
163
|
-
- **No ids, no wiring.** `
|
|
163
|
+
- **No ids, no wiring.** `SheetStack` holds which sheet is open (`open` + `onOpenChange`, or `defaultOpen`).
|
|
164
164
|
- **`.card-stack` is gone.** That was this component's first name and the wrong
|
|
165
165
|
one — the members are sheets and the stack is the menu, not a list of cards.
|
|
166
166
|
Never bring it back, not even as an alias.
|
|
@@ -355,6 +355,10 @@ between at 40% down the screen (see *Anatomy*, "Where the centre is"):
|
|
|
355
355
|
- **Foot** (`.card__foot`): the card's action buttons on one line. One
|
|
356
356
|
button takes the width; up to four share it in even parts; from three
|
|
357
357
|
on they drop their side padding so the labels keep the room.
|
|
358
|
+
- In React the head and the foot are the card's **slots**:
|
|
359
|
+
`<Card heading caption end foot footLink>` with the content as children —
|
|
360
|
+
the card writes the order (head first, foot last). A head of its own shape
|
|
361
|
+
is a `<CardHead>` child instead; never both (Lh 2026-09-27).
|
|
358
362
|
- A page with **one feature and one CTA** (an install page, a single
|
|
359
363
|
decision) may be contained in one card: head, the pitch, foot.
|
|
360
364
|
- **A bottom sheet holds what the member acts on** — controls, or small
|
|
@@ -373,7 +377,7 @@ between at 40% down the screen (see *Anatomy*, "Where the centre is"):
|
|
|
373
377
|
same material — a stacked sheet is written as a `.card`, and the single
|
|
374
378
|
sheet is `.card.sheet` — and both obey rule 8: rigid, full height, moved
|
|
375
379
|
by `translate` only. (The stack was first named "card stack": wrong, and
|
|
376
|
-
retired.)
|
|
380
|
+
retired.) See them live in the gallery: `Sheet`, `SheetStack`.
|
|
377
381
|
- **A step is one screen, one task: `.step`.** What the member reads is
|
|
378
382
|
words on the base (`.page` — the same block as anywhere on the base; it was `.step__card` until 2026-09-16); what the member does, if the step asks for it, is a
|
|
379
383
|
`.sheet` in front of it; the screen's one move is `.action-bar`, the screen's
|
|
@@ -385,8 +389,7 @@ between at 40% down the screen (see *Anatomy*, "Where the centre is"):
|
|
|
385
389
|
slides in: it starts where the top bar ends and ends where the bar's room
|
|
386
390
|
begins. Nobody lays a step out by hand and nobody writes a
|
|
387
391
|
sheet's height. In short: **member acts → card + sheet, member reads →
|
|
388
|
-
card.** Markup: *Step markup*.
|
|
389
|
-
`index.html` "step".
|
|
392
|
+
card.** Markup: *Step markup*. Live: the gallery's `Step` page.
|
|
390
393
|
|
|
391
394
|
- **Where the centre is.** To the person holding a phone, the bottom fifth of
|
|
392
395
|
the screen is the thumb's — the bar, the controls — so what is meant to be
|
|
@@ -395,7 +398,7 @@ between at 40% down the screen (see *Anatomy*, "Where the centre is"):
|
|
|
395
398
|
with one card and nothing else to read puts it there (`.card--centre`, the
|
|
396
399
|
introduction screens); a splash puts its mark there.
|
|
397
400
|
|
|
398
|
-
|
|
401
|
+
Live: every screen in the gallery (`npm run dev`).
|
|
399
402
|
|
|
400
403
|
## Rules of the system
|
|
401
404
|
|
|
@@ -461,7 +464,7 @@ Demo: the *skeleton* block at the top of `elements.html`.
|
|
|
461
464
|
get covered. Closing reverses it in two phases: the after-pack returns
|
|
462
465
|
while the card drops into its slot, then the before-pack slides back up
|
|
463
466
|
from behind. Slots, offsets and paint order are all CSS `:nth-child`, so
|
|
464
|
-
a stack renders correctly with no script; `
|
|
467
|
+
a stack renders correctly with no script; `SheetStack` only sets the
|
|
465
468
|
state classes. Don't reintroduce height, `display` or z-index switching
|
|
466
469
|
here — every past bug in this animation came from exactly that.
|
|
467
470
|
7. **Everything shrinks to 320px.** `.screen > *`, `.card` and `.card > *`
|
|
@@ -488,14 +491,14 @@ Demo: the *skeleton* block at the top of `elements.html`.
|
|
|
488
491
|
revealed above the cut, not under it. `--sheet-half` is a share of the
|
|
489
492
|
sheet's height (`0.5`), one number serving both the translate and that
|
|
490
493
|
padding. Two boxes hold sheets and the states
|
|
491
|
-
mean the same in both: `.step` (an app screen
|
|
494
|
+
mean the same in both: `.step` (an app screen — Anatomy)
|
|
492
495
|
and `.sheet-stage` (a page-level overlay: a sheet over a map, the dim page
|
|
493
496
|
stage `--dim` + `.modal` = `__lift` + `__drawer` — a sheet always stands on the floor, never raised).
|
|
494
497
|
A stage keeps the top bar's line free (`--_top`), so `--full` stops under
|
|
495
498
|
`--header-h` and keeps its corners. Both sheet tokens are published — a
|
|
496
499
|
sheet with a taller head retunes `--sheet-peek` — and registered in
|
|
497
|
-
`tokens.css`, which is how `
|
|
498
|
-
`
|
|
500
|
+
`tokens.css`, which is how `Sheet` reads them to snap a drag. Live: the
|
|
501
|
+
gallery's `Sheet` page.
|
|
499
502
|
9. **Elements inside a card are frameless.** Rows, key-values, pickers,
|
|
500
503
|
gauges and tiles never draw a second card boundary; hairlines
|
|
501
504
|
(`--hairline`) separate rows, `--outline` keeps controls legible on
|
|
@@ -557,7 +560,7 @@ That file is where a project's look lives. Bases and roles:
|
|
|
557
560
|
| what | tokens | moves |
|
|
558
561
|
|---|---|---|
|
|
559
562
|
| room **between** things | `--sp-base` (0.25rem = 4) | every `--sp-N` = base × N → paddings, gaps, the screen's inset, the bar's reserve, the sheet's peek |
|
|
560
|
-
| type | `--fs-base` (
|
|
563
|
+
| type | `--fs-base` (1.375rem = 22 — the body) | every `--fs-*`, `--badge`, `--icon-xs` |
|
|
561
564
|
| corners | `--r-base` (1rem = 16) | every `--r-*`; `0` = square. `--r-chip` stays fully round |
|
|
562
565
|
| the size **of** things | `--tap-base` (3rem = 48) | `--tap*`, `--btn-h`, `--chip-h`, `--bar-h`, `--header-h`, `--fab`, `--icon*`, `--avatar*`, `--dot*`, `--ring*`, `--tile-*`. `--tap*` never under `--tap-floor` (44px) |
|
|
563
566
|
| faces | `--font-ui`, `--font-display` | |
|
|
@@ -612,8 +615,8 @@ Copy any block in `palette.css`, rename, retune:
|
|
|
612
615
|
|
|
613
616
|
Every length token resolves to rem — a multiplier × one of four rem bases
|
|
614
617
|
(`--sp-4` = `--sp-base` × 4 = 1rem, `--tap` = `--tap-base` = 3rem, `--r-card` =
|
|
615
|
-
`--r-base` × 2, `--fs-h1` = `--fs-base` × 1.
|
|
616
|
-
whole system
|
|
618
|
+
`--r-base` × 2, `--fs-h1` = `--fs-base` × 1.2; see "A project's theme") — so the
|
|
619
|
+
whole system scales with the root font size. `base.css` keeps it at `100%` (the browser's setting, 16px by
|
|
617
620
|
default) and steps narrow phones (≤360px) to `93.75%`, which keeps the 25rem
|
|
618
621
|
column, its 48px taps and its type in proportion on 320–360 screens. Only
|
|
619
622
|
`--border-w`, `--rule-w` and the focus ring stay in px, so edges stay crisp.
|
package/cardds.css
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
@import url("css/fonts.css"); /* the two faces: PK Nonthaburi (body), FC Pride (display) */
|
|
3
3
|
@import url("css/tokens.css"); /* structure + wireframe defaults */
|
|
4
4
|
@import url("css/palette.css"); /* color settings — the file you tune */
|
|
5
|
-
@import url("css/base.css"); /* reset, surface, type levels,
|
|
5
|
+
@import url("css/base.css"); /* reset, surface, type levels, page */
|
|
6
6
|
@import url("css/card.css"); /* card boundary + list variants */
|
|
7
7
|
@import url("css/stack.css"); /* sheet stack: .sheet-stack — the menu, sheets stacked, heads peek, tap opens */
|
|
8
8
|
@import url("css/sheet.css"); /* sheet single: .sheet — one card as a bottom sheet, peek / half / full */
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
@import url("css/actions.css"); /* topbar, chips, buttons, CTA pack */
|
|
13
13
|
@import url("css/numbers.css"); /* ring, track, band, dot grid, picker, badge, bars */
|
|
14
14
|
@import url("css/people.css"); /* avatar, avatar stack, chooser row */
|
|
15
|
-
@import url("css/choice.css"); /* check, toggle,
|
|
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
17
|
@import url("css/media.css"); /* cover card, quote, tile grid, fold, mosaic, wave */
|
|
18
|
-
@import url("css/layover.css"); /* ask drawer, popover menu, banner, float bar, callout
|
|
18
|
+
@import url("css/layover.css"); /* ask drawer, popover menu, banner, float bar, callout */
|
package/css/actions.css
CHANGED
|
@@ -55,12 +55,12 @@
|
|
|
55
55
|
max-width: var(--screen-w);
|
|
56
56
|
margin-inline: auto;
|
|
57
57
|
padding-inline: var(--appbar-pad-x, var(--screen-pad)); /* fixed + full-bleed: the screen's own inset, so the bar's contents line up with the title bar under it (Lh 2026-09-15) */
|
|
58
|
-
z-index:
|
|
58
|
+
z-index: var(--z-top); /* topmost — card animations never cover it */
|
|
59
59
|
background: var(--appbar-bg, var(--surface)); /* solid: sliding cards never show through */
|
|
60
60
|
}
|
|
61
61
|
/* fixed, so it covers the top of the screen it sits in — reserve its height
|
|
62
|
-
there, from the same token
|
|
63
|
-
|
|
62
|
+
there, from the same header-h token. The breathing under it is published:
|
|
63
|
+
a .step cancels it to touch the bar. */
|
|
64
64
|
.screen:has(> .appbar) { --header-gap: var(--sp-2); padding-top: calc(var(--header-h) + var(--header-gap)); }
|
|
65
65
|
.appbar__brand { flex: 1; display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
|
|
66
66
|
.appbar__text { display: grid; min-width: 0; flex: 1; } /* pushes trailing button to the right edge */
|
|
@@ -68,11 +68,9 @@
|
|
|
68
68
|
.appbar__who {
|
|
69
69
|
flex: 1; min-width: 0; min-height: var(--tap);
|
|
70
70
|
display: flex; align-items: center; gap: var(--sp-2);
|
|
71
|
-
|
|
71
|
+
margin: 0; text-align: start;
|
|
72
72
|
border-radius: var(--appbar-who-radius, var(--r-chip));
|
|
73
|
-
-webkit-tap-highlight-color: transparent;
|
|
74
73
|
}
|
|
75
|
-
.appbar__who:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
76
74
|
.appbar__title, .appbar__sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.35; } /* clipped lines: room for Thai marks */
|
|
77
75
|
.appbar__title { font: var(--type-label); }
|
|
78
76
|
.appbar__sub { font: var(--type-caption); color: var(--on-surface-muted); }
|
|
@@ -96,7 +94,7 @@
|
|
|
96
94
|
.icon-btn--invert { background: var(--icon-btn-invert-bg, var(--accent)); color: var(--icon-btn-invert-ink, var(--on-accent)); border-color: transparent; }
|
|
97
95
|
.icon-btn--sm { width: var(--icon-btn-sm-size, var(--tap-sm)); height: var(--icon-btn-sm-size, var(--tap-sm)); font-size: var(--fs-title); }
|
|
98
96
|
/* fab: the square floating action, bottom-right of a stage or screen */
|
|
99
|
-
.fab { width: var(--fab); height: var(--fab); border-radius: var(--r-card); position: absolute; right: var(--sp-4); bottom: calc(var(--sp-5) + var(--bar-h) + var(--sp-3)); box-shadow: var(--shadow-float); z-index:
|
|
97
|
+
.fab { width: var(--fab); height: var(--fab); border-radius: var(--r-card); position: absolute; right: var(--sp-4); bottom: calc(var(--sp-5) + var(--bar-h) + var(--sp-3)); box-shadow: var(--shadow-float); z-index: var(--z-fab); }
|
|
100
98
|
|
|
101
99
|
/* ---- buttons ---- */
|
|
102
100
|
.btn {
|
|
@@ -129,7 +127,6 @@
|
|
|
129
127
|
/* disabled keeps the slot — a control that vanishes moves everything else,
|
|
130
128
|
and a pager whose "previous" disappeared on page one would shift the bar
|
|
131
129
|
under the thumb on every page */
|
|
132
|
-
:is(.btn, .icon-btn):disabled { opacity: 0.4; cursor: default; }
|
|
133
130
|
/* a row of buttons sharing the width (twin CTAs inside a card) */
|
|
134
131
|
.btn-row { display: flex; gap: var(--sp-2); }
|
|
135
132
|
.btn-row > .btn { flex: 1; min-width: 0; }
|
|
@@ -169,7 +166,7 @@
|
|
|
169
166
|
/* ---- dropdown: the standard for every select — a pill trigger + .menu popover ----
|
|
170
167
|
<div class="dropdown"><button class="dropdown__btn" popovertarget="f1"><span class="dropdown__label">this week</span><svg class="icon icon--xs"><use href="icons.svg#chevron-down"/></svg></button>
|
|
171
168
|
<div class="menu" id="f1" popover><button class="menu__item" aria-current="true" data-value="week">this week</button>…</div></div>
|
|
172
|
-
|
|
169
|
+
The Dropdown component copies the picked item into the label and reports the change.
|
|
173
170
|
.menu (layover.css) draws the panel; anchor positioning pins it under the trigger. */
|
|
174
171
|
.dropdown { position: relative; min-width: 0; display: inline-grid; }
|
|
175
172
|
.dropdown__btn {
|
|
@@ -188,12 +185,11 @@
|
|
|
188
185
|
font: var(--type-label);
|
|
189
186
|
font-size: var(--fs-body);
|
|
190
187
|
text-align: left;
|
|
191
|
-
cursor: pointer;
|
|
192
188
|
}
|
|
193
189
|
/* muted: the value is still the default (README 13) */
|
|
194
190
|
.dropdown--muted .dropdown__btn { color: var(--dropdown-muted-ink, var(--card-muted)); }
|
|
195
191
|
.dropdown__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
196
|
-
.dropdown__btn > .icon { transition: rotate
|
|
192
|
+
.dropdown__btn > .icon { transition: rotate var(--motion-quick) ease; }
|
|
197
193
|
.dropdown:has(.menu:popover-open) .dropdown__btn > .icon { rotate: 180deg; }
|
|
198
194
|
.dropdown:has(.menu.\:popover-open) .dropdown__btn > .icon { rotate: 180deg; } /* the polyfill's open (layover.css) */
|
|
199
195
|
.dropdown--end .menu[popover] { position-area: block-end span-inline-start; } /* right-aligned trigger: panel grows leftwards (= .menu--end, layover.css) */
|
|
@@ -203,10 +199,7 @@
|
|
|
203
199
|
/* ---- segment (day picker / tab strip) — items share width, shrink on small screens ---- */
|
|
204
200
|
.segment { display: flex; gap: var(--sp-1); }
|
|
205
201
|
.segment__item {
|
|
206
|
-
|
|
207
|
-
background: transparent;
|
|
208
|
-
color: inherit;
|
|
209
|
-
opacity: 0.6;
|
|
202
|
+
opacity: var(--rest);
|
|
210
203
|
border-radius: var(--r-chip);
|
|
211
204
|
padding: var(--sp-2) var(--sp-1);
|
|
212
205
|
font: var(--type-label);
|
|
@@ -230,7 +223,7 @@
|
|
|
230
223
|
:root { --bar-reserve: calc(var(--btn-h) + var(--sp-3) + var(--sp-5) + var(--sp-6)); }
|
|
231
224
|
.action-bar {
|
|
232
225
|
position: relative;
|
|
233
|
-
z-index:
|
|
226
|
+
z-index: var(--z-bar); /* above .sheet-stack sheets, which paint at --level 1-5 */
|
|
234
227
|
max-width: var(--screen-w);
|
|
235
228
|
margin-inline: auto;
|
|
236
229
|
padding: var(--sp-3) var(--sp-4) var(--sp-5);
|
|
@@ -260,10 +253,11 @@
|
|
|
260
253
|
NEVER carry words (Lh 2026-09-16, for good): an arrow is the whole meaning,
|
|
261
254
|
and a word that isn't a direction ("Get started", "Done") is not a pager
|
|
262
255
|
button — that step COMMITS and goes back to the plain .action-bar with one
|
|
263
|
-
wide button. Where you are is said by the .pager__at dots
|
|
264
|
-
that changes (a .screen__centre group, card.css)
|
|
265
|
-
|
|
266
|
-
|
|
256
|
+
wide button. Where you are is said by the .pager__at dots — under the card
|
|
257
|
+
that changes (a .screen__centre group, card.css) for a card walk, never in
|
|
258
|
+
that bar (Lh, 2026-09-12); a walk of words instead floats its dots as the
|
|
259
|
+
bar's own first row (step.css, Lh 2026-09-23) — either way the dots move
|
|
260
|
+
with what moves. Between the buttons goes a "skip" link or nothing.
|
|
267
261
|
|
|
268
262
|
<div class="action-bar action-bar--pager">
|
|
269
263
|
<button class="icon-btn" aria-label="ย้อนกลับ" disabled>…</button>
|
|
@@ -280,14 +274,14 @@
|
|
|
280
274
|
height: var(--dot);
|
|
281
275
|
border-radius: 50%;
|
|
282
276
|
background: currentColor;
|
|
283
|
-
opacity:
|
|
277
|
+
opacity: var(--faint);
|
|
284
278
|
}
|
|
285
279
|
.pager__at > .is-at { background: var(--accent); opacity: 1; }
|
|
286
280
|
/* ---- tiers: a control row above the buttons ----
|
|
287
281
|
A screen whose one move comes with a control — the introduction's text-size
|
|
288
282
|
slider above its pager. Each tier is a row; the bar stacks them, and any
|
|
289
283
|
parent hosting the bar publishes the room for both, the way the plain
|
|
290
|
-
reserve is published above, so a .
|
|
284
|
+
reserve is published above, so a .card--centre stops clear of it.
|
|
291
285
|
<div class="action-bar action-bar--tiers">
|
|
292
286
|
<div class="action-bar__tier"><label class="slider">…</label></div>
|
|
293
287
|
<div class="action-bar__tier action-bar__tier--pager">…the round pager…</div>
|
|
@@ -334,25 +328,53 @@
|
|
|
334
328
|
.action-note__words { display: block; } /* ONE run of words: an icon inside sits in the line, the spaces around it kept */
|
|
335
329
|
.action-note__words > .icon { display: inline-block; vertical-align: -0.2em; width: 1.25em; height: 1.25em; margin-inline: 0.15em; color: var(--action-note-icon-ink, inherit); } /* a project may colour the icon apart from the words — e.g. the system blue of the control it points at (Lh 2026-09-24) */
|
|
336
330
|
|
|
337
|
-
/* PRESS (Lh 2026-09-25): every CONTROL answers a tap
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
331
|
+
/* PRESS (Lh 2026-09-25; every tappable thing, 2026-09-27): every CONTROL answers a tap, INFORMATION never does (no pointer, no
|
|
332
|
+
press). Two answers, by size: GIVE — a control the size of a finger shrinks under it (--press-scale) and springs back; DIM —
|
|
333
|
+
something as wide as its card (a row, a summary line, a card, a step bar, a peeked sheet) fades under the finger (--press-dim)
|
|
334
|
+
instead, because a whole row shrinking reads as the card moving. A choice's fill fades in as it is picked. Disabled = no
|
|
335
|
+
pointer, no press, --off. Zero specificity everywhere: a component's own rule always wins. A NEW control joins a list here. */
|
|
336
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
337
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
338
|
+
a.avatar, button.avatar, .menu__item) {
|
|
341
339
|
cursor: pointer;
|
|
342
340
|
touch-action: manipulation;
|
|
343
341
|
transition: scale var(--motion-press) ease-out;
|
|
344
342
|
}
|
|
345
|
-
|
|
346
|
-
:
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
:where(.btn, .icon-btn, .fab, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item,
|
|
350
|
-
.calendar__day, .float-bar__item, .avatar-pick__item, .add-row, .picker__item, .sheet-stack__back, .dropdown__btn, .appbar__who, .menu__item):active:not(:disabled, [aria-disabled="true"]) {
|
|
351
|
-
scale: var(--press-scale);
|
|
343
|
+
:where(a.row, button.row, label.row, .row-expand > summary, .banner__summary, a.card, label.card, .toggle-field) {
|
|
344
|
+
cursor: pointer;
|
|
345
|
+
touch-action: manipulation;
|
|
346
|
+
transition: opacity var(--motion-press) ease-out;
|
|
352
347
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
:
|
|
357
|
-
|
|
348
|
+
/* a CHOICE's fill fades in as it is picked (a plain button's colour never changes on a tap, so it does not fade) */
|
|
349
|
+
:where(.chip--pick, button.chip, .tile--pick, button.tile, .day, .segment__item, .calendar__day, .float-bar__item, .picker__item, .check,
|
|
350
|
+
.icon-btn[role="tab"], .avatar-pick__item > .avatar) {
|
|
351
|
+
transition-property: scale, background-color, color, border-color, opacity;
|
|
352
|
+
transition-duration: var(--motion-press);
|
|
353
|
+
transition-timing-function: ease-out;
|
|
358
354
|
}
|
|
355
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
356
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
357
|
+
a.avatar, button.avatar, .menu__item):active:not(:disabled, [aria-disabled="true"], :has(:disabled)) { scale: var(--press-scale); }
|
|
358
|
+
:where(a.row, button.row, label.row, .row-expand > summary, .banner__summary, a.card, label.card, .toggle-field):active:not([aria-disabled="true"], :has(:disabled)) { opacity: var(--press-dim); }
|
|
359
|
+
.sheet-step:has(> .sheet-step__go:active) { opacity: var(--press-dim); } /* the step bar's button is invisible over it: the bar answers */
|
|
360
|
+
/* SHADE — what has something BEHIND it (a peeked sheet over the pile, a postcard's face over its back) must not turn see-through:
|
|
361
|
+
it darkens a touch instead (--press-shade) */
|
|
362
|
+
:where(.sheet-stack--tap > .card:not(.is-open), .postcard__turn) { cursor: pointer; touch-action: manipulation; transition: filter var(--motion-press) ease-out; }
|
|
363
|
+
:where(.sheet-stack--tap > .card:not(.is-open), .postcard__turn):active { filter: brightness(var(--press-shade)); }
|
|
364
|
+
/* the focus ring: on the control, or on the label that wraps a hidden input (chip, tile, file button, a row that is a label) */
|
|
365
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
366
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
367
|
+
a.avatar, button.avatar, .menu__item):focus-visible,
|
|
368
|
+
:where(.chip--pick, .tile--pick, label.btn, label.row, .toggle-field):has(:focus-visible) { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
369
|
+
:where(a.row, button.row, label.row, .row-expand > summary, .banner__summary, a.card, label.card, .toggle-field):not(label, .toggle-field):focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
370
|
+
/* disabled: no pointer anywhere; a control fades (a link and a row keep their own muted look) */
|
|
371
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
372
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
373
|
+
a.avatar, button.avatar, .menu__item, a.row, button.row, label.row, .row-expand > summary, .banner__summary, a.card, label.card, .toggle-field):is(:disabled, [aria-disabled="true"]),
|
|
374
|
+
:where(.chip--pick, .tile--pick, .mood__opt, .toggle-field, label.btn, label.row):has(:disabled) { cursor: default; }
|
|
375
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
376
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
377
|
+
a.avatar, button.avatar, .menu__item):not(.link):is(:disabled, [aria-disabled="true"]),
|
|
378
|
+
:where(.chip--pick, .tile--pick, .mood__opt, .toggle-field, label.btn):has(:disabled) { opacity: var(--off); }
|
|
379
|
+
.sheet-stack--tap > .card > .card__head > :is(h1, h2, h3):focus-visible { outline: var(--focus); outline-offset: var(--sp-1); } /* the keyboard's way into a stack sheet */
|
|
380
|
+
@media (prefers-reduced-motion: reduce) { :root { --press-scale: 1; } } /* the give goes; the dim, the shade and the focus ring stay */
|
package/css/base.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
|
-
cardds/base.css — reset, surface, typography levels,
|
|
2
|
+
cardds/base.css — reset, surface, typography levels, page
|
|
3
3
|
============================================================ */
|
|
4
4
|
|
|
5
5
|
*, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
|
@@ -21,9 +21,8 @@ button, [role="button"], [role="tab"], [role="option"], [role="menuitem"], a.btn
|
|
|
21
21
|
An app's OWN text-size setting is NOT here (Lh 2026-09-22: --text-scale, which
|
|
22
22
|
multiplied the root, zoomed the UI too — retired): it is --fs-base on <html>,
|
|
23
23
|
the type base, so the words grow and the things keep their size (tokens.css). */
|
|
24
|
-
html { font-size: 100%;
|
|
24
|
+
html { font-size: 100%; }
|
|
25
25
|
@media (max-width: 22.5em) { html { font-size: 93.75%; } }
|
|
26
|
-
html::-webkit-scrollbar { display: none; } /* Chromium/Safari */
|
|
27
26
|
|
|
28
27
|
body {
|
|
29
28
|
margin: 0;
|
|
@@ -39,7 +38,30 @@ body {
|
|
|
39
38
|
[hidden] { display: none !important; }
|
|
40
39
|
|
|
41
40
|
img, svg { display: block; max-width: 100%; }
|
|
42
|
-
button { font: inherit;
|
|
41
|
+
button { font: inherit; }
|
|
42
|
+
button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
43
|
+
|
|
44
|
+
/* zero-specificity reset for controls that draw nothing of their own (2026-09-27 review):
|
|
45
|
+
a component's own rule always wins, at any specificity, so this never fights it — it only
|
|
46
|
+
fills in for a selector that never says otherwise. Not everything below shares every
|
|
47
|
+
property: two exceptions read as they always have — .calendar__day has never set its own
|
|
48
|
+
padding (the UA button default), and .sheet-step__go has never set its own color (the UA
|
|
49
|
+
button default too) — giving either one now would be a look change, so neither is here. */
|
|
50
|
+
:where(.appbar__who, .segment__item, .day, .calendar__day, .menu__item, .float-bar__item, .link,
|
|
51
|
+
.add-row, .avatar-pick__item, .picker__item, .postcard__turn, .sheet-step__go) {
|
|
52
|
+
border: 0;
|
|
53
|
+
background: transparent;
|
|
54
|
+
font: inherit;
|
|
55
|
+
}
|
|
56
|
+
:where(.appbar__who, .segment__item, .day, .calendar__day, .menu__item, .float-bar__item, .link,
|
|
57
|
+
.add-row, .avatar-pick__item, .picker__item, .postcard__turn) { color: inherit; }
|
|
58
|
+
:where(.appbar__who, .segment__item, .day, .menu__item, .float-bar__item, .link, .add-row,
|
|
59
|
+
.avatar-pick__item, .picker__item, .postcard__turn, .sheet-step__go) { padding: 0; }
|
|
60
|
+
|
|
61
|
+
/* scrollbar hidden, scroll kept: the track scrolls by touch/drag, never by a visible bar —
|
|
62
|
+
.bleed (card.css) is the one exception, its bar is designed, not hidden */
|
|
63
|
+
:where(html, .sheet-stack--tap > .card, .step > .pages, .picker__rail, .avatar-pick) { scrollbar-width: none; }
|
|
64
|
+
:where(html, .sheet-stack--tap > .card, .step > .pages, .picker__rail, .avatar-pick)::-webkit-scrollbar { display: none; }
|
|
43
65
|
|
|
44
66
|
/* icon from icons.svg sprite: <svg class="icon"><use href="icons.svg#bell"/></svg> */
|
|
45
67
|
.icon { width: var(--icon-size, var(--icon)); height: var(--icon-size, var(--icon)); flex: none; }
|
|
@@ -48,10 +70,16 @@ button { font: inherit; cursor: pointer; }
|
|
|
48
70
|
.icon--lg { width: var(--icon-lg-size, var(--icon-lg)); height: var(--icon-lg-size, var(--icon-lg)); } /* empty states */
|
|
49
71
|
|
|
50
72
|
/* mobile screen column — the standard horizontal inset, and nothing vertical.
|
|
51
|
-
Top
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
73
|
+
Top is zero on purpose: room there is only ever a RESERVE for something
|
|
74
|
+
FIXED parked over the screen — .appbar reserves it (actions.css). A bottom
|
|
75
|
+
bar is not fixed (0.7.1): it is the Screen's own last child, in flow on a
|
|
76
|
+
scrolling screen (a sticky row, actions.css) or, in a fill screen, a layer
|
|
77
|
+
over the content's foot (step.css) — nothing here reserves room for it.
|
|
78
|
+
It also reads like a native app (Lh 2026-09-25): words are not highlighted
|
|
79
|
+
by a long press or a drag, no copy / web-search callout — only what is
|
|
80
|
+
typed into, and words marked selectable (`Text selectable` → .t-select: a
|
|
81
|
+
code, an address). Knob --screen-select: `text` = the web page's old way
|
|
82
|
+
everywhere. */
|
|
55
83
|
.screen {
|
|
56
84
|
--screen-gap: var(--sp-5); /* published: a .step cancels it to touch the bar it follows (step.css) */
|
|
57
85
|
max-width: var(--screen-w);
|
|
@@ -60,6 +88,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
60
88
|
display: grid;
|
|
61
89
|
gap: var(--screen-gap);
|
|
62
90
|
align-content: start; /* a screen taller than its content (a fixed phone) never spreads its rows — the top bar stays at the top, a row keeps its height */
|
|
91
|
+
-webkit-user-select: var(--screen-select, none); user-select: var(--screen-select, none); -webkit-touch-callout: none;
|
|
63
92
|
}
|
|
64
93
|
.screen > * { min-width: 0; } /* content never widens the column past the viewport */
|
|
65
94
|
/* a screen that carries its own palette (data-palette on the element, not on <html>)
|
|
@@ -102,21 +131,18 @@ button { font: inherit; cursor: pointer; }
|
|
|
102
131
|
|
|
103
132
|
.t-muted { color: var(--on-surface-muted); }
|
|
104
133
|
.card .t-muted { color: var(--card-muted); } /* muted follows the card's ink, not the surface's */
|
|
105
|
-
.card--3 .t-muted
|
|
134
|
+
/* .card--3 .t-muted (dim instead of --card-muted on the dark slot) is declared once in card.css, with the rest of that family */
|
|
106
135
|
.t-caps { text-transform: uppercase; }
|
|
107
136
|
.grow { flex: 1; min-width: 0; } /* the child that takes the remaining row space */
|
|
108
137
|
/* read, not shown (Lh 2026-09-23): words a screen reader needs — a page's h1 the design leaves out — kept in the page, taking no room.
|
|
109
138
|
The 1px is the one the platform needs to keep it in the accessibility tree, not a length of the design. */
|
|
110
|
-
/* a screen reads like a native app (Lh 2026-09-25): words are not highlighted by a long press or a drag, no copy / web-search
|
|
111
|
-
callout — only what is typed into, and words marked selectable (`Text selectable` → .t-select: a code, an address).
|
|
112
|
-
Knob --screen-select: `text` = the web page's old way everywhere. */
|
|
113
|
-
.screen { -webkit-user-select: var(--screen-select, none); user-select: var(--screen-select, none); -webkit-touch-callout: none; }
|
|
114
139
|
.screen :is(input, textarea, [contenteditable="true"]), .screen .t-select { -webkit-user-select: text; user-select: text; -webkit-touch-callout: default; }
|
|
115
140
|
.t-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
|
|
116
141
|
|
|
117
|
-
/* ----
|
|
118
|
-
|
|
119
|
-
|
|
142
|
+
/* ---- page: words straight on the base, no card around them — a
|
|
143
|
+
heading, a paragraph, maybe a .cta-pack. Was .section until 2026-09-16
|
|
144
|
+
(Lh: the name said nothing about WHERE it lives), then .base-content
|
|
145
|
+
until 2026-09-23 (Lh: the content is a BOX; what's in it is .page).
|
|
120
146
|
In a .step it is the reading block and step.css adds the flex/scroll rule ---- */
|
|
121
147
|
.page {
|
|
122
148
|
display: grid;
|
|
@@ -150,6 +176,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
150
176
|
color: var(--empty-state-ink, var(--on-surface)); /* full ink, icon and words alike: muted read as "disabled" at the middle of an empty screen (Lh 2026-09-16) — muted is for meta, not a sentence */
|
|
151
177
|
}
|
|
152
178
|
.empty-state__action { margin-top: var(--sp-2); }
|
|
153
|
-
/* inside a card (a
|
|
179
|
+
/* inside a card (a card's own "nothing yet"): the card's ink */
|
|
154
180
|
.card .empty-state { color: var(--card-ink); padding-block: var(--sp-6); }
|
|
155
181
|
.card--3 .empty-state { color: inherit; }
|
package/css/card.css
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
cardds/card.css — the card boundary + list variants
|
|
3
|
-
One card = one boundary.
|
|
3
|
+
One card = one boundary. The base .card is tone 1; further tones
|
|
4
|
+
via .card--2/3/4/5 (role slots).
|
|
4
5
|
============================================================ */
|
|
5
6
|
|
|
6
7
|
.card {
|
|
@@ -25,14 +26,18 @@
|
|
|
25
26
|
.card > * { min-width: 0; }
|
|
26
27
|
|
|
27
28
|
.card--2 { --card-bg: var(--card-2-bg); --card-ink: var(--card-2-ink); --card-muted: var(--card-2-muted); }
|
|
28
|
-
.card--3 { --card-bg: var(--card-3-bg); --card-ink: var(--card-3-ink); }
|
|
29
|
-
.card--4 { --card-bg: var(--card-4-bg); --card-ink: var(--card-4-ink); --card-muted: var(--card-4-muted); }
|
|
30
|
-
.card--5 { --card-bg: var(--card-5-bg); --card-ink: var(--card-5-ink); --card-muted: var(--card-5-muted); }
|
|
31
|
-
|
|
32
29
|
/* dark/accent slot: accent-toned controls would vanish — the slot re-points
|
|
33
30
|
the accent tokens, so every accent consumer (btn--primary, chip--on,
|
|
34
31
|
check, toggle, ring, day…) inverts without per-component rules */
|
|
35
|
-
.card--3 { --accent: var(--card-3-ink); --on-accent: var(--card-3-bg); }
|
|
32
|
+
.card--3 { --card-bg: var(--card-3-bg); --card-ink: var(--card-3-ink); --accent: var(--card-3-ink); --on-accent: var(--card-3-bg); }
|
|
33
|
+
.card--4 { --card-bg: var(--card-4-bg); --card-ink: var(--card-4-ink); --card-muted: var(--card-4-muted); }
|
|
34
|
+
.card--5 { --card-bg: var(--card-5-bg); --card-ink: var(--card-5-ink); --card-muted: var(--card-5-muted); }
|
|
35
|
+
/* the dark slot's captions/hints/footnotes: --card-muted is tuned for a light card, so these dim the full ink
|
|
36
|
+
instead (one rule, matching specificity to what it replaces — .card--3 <space> <one class>) — merged from
|
|
37
|
+
base.css/choice.css/media.css/numbers.css/journey.css/people.css (2026-09-27 review). lists.css's .legend
|
|
38
|
+
differs (opacity 0.8, not --dim) and stays there. */
|
|
39
|
+
.card--3 :is(.t-muted, .calendar__wd, .composer__hint, .quote__by, .ring__label, .band__foot, .picker__unit,
|
|
40
|
+
.route__place, .note-row, .avatar-pick__role) { color: inherit; opacity: var(--dim); }
|
|
36
41
|
|
|
37
42
|
/* header row: title left, floating action right — centered, since the
|
|
38
43
|
trailing control (chip/badge/icon-btn/link) is almost always taller than
|
|
@@ -135,7 +140,7 @@
|
|
|
135
140
|
linear-gradient(to top left, transparent calc(50% - 1px), currentColor calc(50% - 1px), currentColor calc(50% + 1px), transparent calc(50% + 1px));
|
|
136
141
|
border: var(--border-w) solid currentColor;
|
|
137
142
|
border-radius: var(--r-media);
|
|
138
|
-
opacity:
|
|
143
|
+
opacity: var(--off);
|
|
139
144
|
min-height: 10rem;
|
|
140
145
|
}
|
|
141
146
|
.ph--sm { min-height: 7.5rem; }
|
|
@@ -191,9 +196,9 @@
|
|
|
191
196
|
likes when not (Lh, 2026-09-12) ----
|
|
192
197
|
The member sees one content area — whatever is between the top bar and the
|
|
193
198
|
bar — and reads it as a whole page: a short card sits at its middle, a long
|
|
194
|
-
one simply makes the page longer, runs under the bar (
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
one simply makes the page longer, runs under the bar (a sticky last row,
|
|
200
|
+
actions.css) and scrolls up like any page — nothing is capped, nothing
|
|
201
|
+
shrinks, no scroll box inside a box.
|
|
197
202
|
So on a .screen that is NOT --fill the group is in flow: the column is at
|
|
198
203
|
least one phone tall, the group takes the room after the top bar and
|
|
199
204
|
centres its children in it. The screen's own inset stays the inset.
|
|
@@ -211,9 +216,9 @@
|
|
|
211
216
|
the React adapter's slideTo() sets data-slide on :root, starts the transition
|
|
212
217
|
and updates inside it; a browser without them (the floor: Chrome 105 / iOS 16)
|
|
213
218
|
simply switches. Only the card is named; the rest of the page must not
|
|
214
|
-
cross-fade.
|
|
215
|
-
|
|
216
|
-
|
|
219
|
+
cross-fade. The name is set by slideTo() on the moving element for the move only (2026-09-22): a static name on
|
|
220
|
+
every walk's current page made two walks on one page — the gallery — share it, and the browser aborts a
|
|
221
|
+
transition with a duplicate name. */
|
|
217
222
|
|
|
218
223
|
/* ---- pages: the cards of a walk, all one height ----
|
|
219
224
|
A sequence the member walks (an introduction) reads steadier when every card
|