@usableapp/cardds 0.7.15 → 0.7.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/README.md +29 -28
- package/cardds.css +3 -3
- package/css/actions.css +67 -22
- package/css/base.css +44 -18
- package/css/card.css +25 -26
- package/css/choice.css +21 -33
- package/css/forms.css +0 -3
- package/css/journey.css +1 -3
- package/css/layover.css +19 -41
- package/css/lists.css +1 -6
- package/css/media.css +9 -18
- package/css/numbers.css +6 -21
- package/css/people.css +3 -9
- package/css/sheet.css +15 -19
- package/css/stack.css +21 -29
- package/css/step.css +7 -12
- package/css/theme.template.css +2 -1
- package/css/tokens.css +22 -4
- package/dist/actions/Btn.d.ts +1 -1
- package/dist/actions/Dropdown.js +3 -9
- package/dist/actions/Fab.d.ts +1 -1
- package/dist/actions/Fab.js +3 -2
- package/dist/cardds.css +264 -267
- package/dist/cards/Card.d.ts +24 -8
- package/dist/cards/Card.js +23 -8
- package/dist/cards/CardHead.d.ts +0 -1
- package/dist/cards/CardHead.js +1 -1
- package/dist/choice/Calendar.js +2 -5
- package/dist/choice/Mood.d.ts +10 -5
- package/dist/choice/Mood.js +4 -2
- package/dist/forms/Field.js +2 -3
- package/dist/forms/FileBtn.js +1 -1
- package/dist/forms/Pin.js +7 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/layover/Banner.d.ts +10 -3
- package/dist/layover/Banner.js +6 -3
- package/dist/lists/Row.d.ts +2 -1
- package/dist/lists/Row.js +5 -5
- package/dist/media/Postcard.js +3 -5
- package/dist/numbers/Band.d.ts +3 -1
- package/dist/numbers/Picker.js +3 -10
- package/dist/numbers/Track.d.ts +4 -2
- package/dist/numbers/Track.js +3 -3
- package/dist/people/AvatarPick.d.ts +3 -1
- package/dist/people/AvatarPick.js +2 -2
- package/dist/scaffold/ActionBar.d.ts +3 -3
- package/dist/scaffold/ActionBar.js +2 -2
- package/dist/scaffold/Centre.d.ts +2 -2
- package/dist/scaffold/Centre.js +2 -2
- package/dist/scaffold/PagerAt.d.ts +1 -1
- package/dist/scaffold/PagerAt.js +1 -1
- package/dist/scaffold/Screen.js +4 -7
- package/dist/scaffold/TopBar.js +3 -5
- package/dist/sheets/Drawer.js +3 -4
- package/dist/sheets/Sheet.js +9 -22
- package/dist/sheets/SheetStack.d.ts +2 -2
- package/dist/sheets/SheetStack.js +20 -13
- package/dist/sheets/SheetSteps.js +8 -11
- package/dist/sheets/StackBack.js +2 -1
- package/dist/sheets/StackSheet.d.ts +3 -2
- package/dist/sheets/StackSheet.js +7 -11
- package/dist/state.d.ts +24 -0
- package/dist/state.js +57 -0
- package/dist/templates/CallSheet.js +2 -4
- package/dist/templates/Home.d.ts +3 -2
- package/dist/templates/Home.js +2 -4
- package/dist/templates/Splash.js +3 -3
- package/dist/templates/Walkthrough.js +4 -5
- package/dist/type/Icon.js +3 -5
- package/dist/type/Text.d.ts +1 -1
- package/dist/type/textScale.d.ts +6 -0
- package/dist/type/textScale.js +12 -0
- package/package.json +7 -4
- package/dist/layover/Deck.d.ts +0 -9
- package/dist/layover/Deck.js +0 -10
package/dist/cardds.css
CHANGED
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
--btn-h: calc(var(--tap-base) * 13 / 12); /* 52 button height */
|
|
90
90
|
--btn-xl-h: calc(var(--tap-base) * 11 / 6); /* 88 the one giant CTA */
|
|
91
91
|
--chip-h: calc(var(--tap-base) * 19 / 24); /* 38 chip height */
|
|
92
|
-
--bar-h: calc(var(--tap-base) * 4 / 3); /* 64 float bar, day cell
|
|
92
|
+
--bar-h: calc(var(--tap-base) * 4 / 3); /* 64 float bar, day cell */
|
|
93
93
|
--header-h: calc(var(--tap-base) * 5 / 4); /* 60 top bar — .topbar and .appbar alike */
|
|
94
94
|
--card-pad-rest: var(--sp-5); /* 20 a card's padding before any variant — .card publishes it as --card-pad; the sheet's peek strip is built from it where no card is in scope */
|
|
95
95
|
--card-gap-rest: var(--sp-4); /* 16 …and its gap */
|
|
@@ -134,8 +134,26 @@
|
|
|
134
134
|
|
|
135
135
|
/* ---- motion ---- */
|
|
136
136
|
--motion-sheet: 0.35s;
|
|
137
|
+
--motion-quick: 0.2s; /* a small state flip that isn't a press: a chevron turning, a popover/banner opening */
|
|
137
138
|
--motion-slide: 0.22s; /* a centred card sliding out and the next in (card.css) — quick, a page turn (Lh, 2026-09-12) */
|
|
138
139
|
--motion-flip: 0.45s; /* a postcard turning over (media.css) — slow enough to read as a card in the hand */
|
|
140
|
+
--dim: 0.7; /* secondary words dimmed on a ground whose muted ink does not fit (the dark card --3, a drawer, a tile's sub line): ONE share for all */
|
|
141
|
+
--rest: 0.6; /* an unpicked choice at rest: .day, .segment__item, .float-bar__item */
|
|
142
|
+
--faint: 0.3; /* further still: rails, far dots, unselected picker items */
|
|
143
|
+
--motion-press: 0.12s; /* a control giving under the finger and springing back (actions.css PRESS) */
|
|
144
|
+
--press-scale: 0.96; /* how far a finger-sized control gives — a share of its size */
|
|
145
|
+
--press-dim: 0.6; /* how far a card-wide one (a row, a summary line, a card) fades under the finger */
|
|
146
|
+
--press-shade: 0.93; /* how far something with a thing behind it (a peeked sheet, a postcard) darkens under the finger */
|
|
147
|
+
--off: 0.4; /* a disabled control's fade */
|
|
148
|
+
|
|
149
|
+
/* ---- z-index: the few layers that stack ABOVE ordinary content, low to high ---- */
|
|
150
|
+
--z-bar: 30; /* .action-bar, .float-bar — above .sheet-stack sheets (--level 1-5) */
|
|
151
|
+
--z-fab: 31; /* .fab — above the bars it floats near */
|
|
152
|
+
--z-top: 40; /* .appbar, a stage laid OVER its screen (.sheet-stage--over) — topmost, card animations never cover it */
|
|
153
|
+
|
|
154
|
+
/* ---- shared private sizes (not a knob: two components share the exact same
|
|
155
|
+
literal by design and must not drift apart) ---- */
|
|
156
|
+
--_cover-min-h: 22.5rem; /* .card--cover's min-height; --postcard-min-h falls back to it (media.css) */
|
|
139
157
|
|
|
140
158
|
/* ---- typography: one scale, rem so it follows the root ---- */
|
|
141
159
|
--font-ui: "PK Nonthaburi", -apple-system, system-ui, sans-serif; /* fonts.css ships it; the rest is the fallback while it loads */
|
|
@@ -250,7 +268,7 @@
|
|
|
250
268
|
|
|
251
269
|
/* ============================================================
|
|
252
270
|
REGISTERED TOKENS — the two sheet lengths are typed, so that
|
|
253
|
-
|
|
271
|
+
the Sheet component can read them back RESOLVED: getComputedStyle returns a
|
|
254
272
|
registered <length> in px and a <number> as a number, where an
|
|
255
273
|
unregistered custom property only ever reads back as its text
|
|
256
274
|
("calc(1.25rem + …)"). Drag-to-snap needs to know where the states
|
|
@@ -262,8 +280,8 @@
|
|
|
262
280
|
falls back to when its own declaration cannot resolve.
|
|
263
281
|
============================================================ */
|
|
264
282
|
@property --sheet-peek { syntax: "<length>"; inherits: true; initial-value: 0px; }
|
|
265
|
-
@property --sheet-head-h { syntax: "<length>"; inherits: true; initial-value: 48px; }
|
|
266
|
-
@property --_lead { syntax: "<length>"; inherits: true; initial-value: 0px; } /* the room above a step's words: registered so a cqh share (a no-bar step, 15% of the SCREEN) resolves where it is declared — on the step, against the screen — not on the words, against the step */
|
|
283
|
+
@property --sheet-head-h { syntax: "<length>"; inherits: true; initial-value: 48px; } /* the sheet head's floor (sheet.css) — registered so the Sheet reads it resolved */
|
|
284
|
+
@property --_lead { syntax: "<length>"; inherits: true; initial-value: 0px; } /* the room above a step's words: registered so a cqh share (a no-bar step, 15% of the SCREEN) resolves where it is declared — on the step, against the screen — not on the words, against the step */
|
|
267
285
|
@property --sheet-half { syntax: "<number>"; inherits: true; initial-value: 0.5; }
|
|
268
286
|
@property --sheet-3q { syntax: "<number>"; inherits: true; initial-value: 0.25; }
|
|
269
287
|
|
|
@@ -427,9 +445,9 @@
|
|
|
427
445
|
/* …but controls keep a faint edge so they still read as controls */
|
|
428
446
|
[data-palette] { --outline: color-mix(in srgb, currentColor 25%, transparent); }
|
|
429
447
|
|
|
430
|
-
/* ---- css/base.css — reset, surface, type levels,
|
|
448
|
+
/* ---- css/base.css — reset, surface, type levels, page ---- */
|
|
431
449
|
/* ============================================================
|
|
432
|
-
cardds/base.css — reset, surface, typography levels,
|
|
450
|
+
cardds/base.css — reset, surface, typography levels, page
|
|
433
451
|
============================================================ */
|
|
434
452
|
|
|
435
453
|
*, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
|
@@ -439,7 +457,7 @@
|
|
|
439
457
|
the member wants to copy), and inputs are untouched. Native buttons and
|
|
440
458
|
links are matched by element; the rest by the class that makes them one */
|
|
441
459
|
button, [role="button"], [role="tab"], [role="option"], [role="menuitem"], a.btn, a.icon-btn, a.chip, label.chip, .chip--pick,
|
|
442
|
-
.segment__item, .day, .calendar__day, .mood__opt, .pin__cell, .slider, .picker__item, .float-bar__item, .menu__item, .sheet__handle, .
|
|
460
|
+
.segment__item, .day, .calendar__day, .mood__opt, .pin__cell, .slider, .picker__item, .float-bar__item, .menu__item, .sheet__handle, .track__step, .pager__at {
|
|
443
461
|
user-select: none;
|
|
444
462
|
-webkit-user-select: none;
|
|
445
463
|
-webkit-touch-callout: none;
|
|
@@ -451,9 +469,8 @@ button, [role="button"], [role="tab"], [role="option"], [role="menuitem"], a.btn
|
|
|
451
469
|
An app's OWN text-size setting is NOT here (Lh 2026-09-22: --text-scale, which
|
|
452
470
|
multiplied the root, zoomed the UI too — retired): it is --fs-base on <html>,
|
|
453
471
|
the type base, so the words grow and the things keep their size (tokens.css). */
|
|
454
|
-
html { font-size: 100%;
|
|
472
|
+
html { font-size: 100%; }
|
|
455
473
|
@media (max-width: 22.5em) { html { font-size: 93.75%; } }
|
|
456
|
-
html::-webkit-scrollbar { display: none; } /* Chromium/Safari */
|
|
457
474
|
|
|
458
475
|
body {
|
|
459
476
|
margin: 0;
|
|
@@ -469,7 +486,30 @@ body {
|
|
|
469
486
|
[hidden] { display: none !important; }
|
|
470
487
|
|
|
471
488
|
img, svg { display: block; max-width: 100%; }
|
|
472
|
-
button { font: inherit;
|
|
489
|
+
button { font: inherit; }
|
|
490
|
+
button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
|
|
491
|
+
|
|
492
|
+
/* zero-specificity reset for controls that draw nothing of their own (2026-09-27 review):
|
|
493
|
+
a component's own rule always wins, at any specificity, so this never fights it — it only
|
|
494
|
+
fills in for a selector that never says otherwise. Not everything below shares every
|
|
495
|
+
property: two exceptions read as they always have — .calendar__day has never set its own
|
|
496
|
+
padding (the UA button default), and .sheet-step__go has never set its own color (the UA
|
|
497
|
+
button default too) — giving either one now would be a look change, so neither is here. */
|
|
498
|
+
:where(.appbar__who, .segment__item, .day, .calendar__day, .menu__item, .float-bar__item, .link,
|
|
499
|
+
.add-row, .avatar-pick__item, .picker__item, .postcard__turn, .sheet-step__go) {
|
|
500
|
+
border: 0;
|
|
501
|
+
background: transparent;
|
|
502
|
+
font: inherit;
|
|
503
|
+
}
|
|
504
|
+
:where(.appbar__who, .segment__item, .day, .calendar__day, .menu__item, .float-bar__item, .link,
|
|
505
|
+
.add-row, .avatar-pick__item, .picker__item, .postcard__turn) { color: inherit; }
|
|
506
|
+
:where(.appbar__who, .segment__item, .day, .menu__item, .float-bar__item, .link, .add-row,
|
|
507
|
+
.avatar-pick__item, .picker__item, .postcard__turn, .sheet-step__go) { padding: 0; }
|
|
508
|
+
|
|
509
|
+
/* scrollbar hidden, scroll kept: the track scrolls by touch/drag, never by a visible bar —
|
|
510
|
+
.bleed (card.css) is the one exception, its bar is designed, not hidden */
|
|
511
|
+
:where(html, .sheet-stack--tap > .card, .step > .pages, .picker__rail, .avatar-pick) { scrollbar-width: none; }
|
|
512
|
+
:where(html, .sheet-stack--tap > .card, .step > .pages, .picker__rail, .avatar-pick)::-webkit-scrollbar { display: none; }
|
|
473
513
|
|
|
474
514
|
/* icon from icons.svg sprite: <svg class="icon"><use href="icons.svg#bell"/></svg> */
|
|
475
515
|
.icon { width: var(--icon-size, var(--icon)); height: var(--icon-size, var(--icon)); flex: none; }
|
|
@@ -478,10 +518,16 @@ button { font: inherit; cursor: pointer; }
|
|
|
478
518
|
.icon--lg { width: var(--icon-lg-size, var(--icon-lg)); height: var(--icon-lg-size, var(--icon-lg)); } /* empty states */
|
|
479
519
|
|
|
480
520
|
/* mobile screen column — the standard horizontal inset, and nothing vertical.
|
|
481
|
-
Top
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
521
|
+
Top is zero on purpose: room there is only ever a RESERVE for something
|
|
522
|
+
FIXED parked over the screen — .appbar reserves it (actions.css). A bottom
|
|
523
|
+
bar is not fixed (0.7.1): it is the Screen's own last child, in flow on a
|
|
524
|
+
scrolling screen (a sticky row, actions.css) or, in a fill screen, a layer
|
|
525
|
+
over the content's foot (step.css) — nothing here reserves room for it.
|
|
526
|
+
It also reads like a native app (Lh 2026-09-25): words are not highlighted
|
|
527
|
+
by a long press or a drag, no copy / web-search callout — only what is
|
|
528
|
+
typed into, and words marked selectable (`Text selectable` → .t-select: a
|
|
529
|
+
code, an address). Knob --screen-select: `text` = the web page's old way
|
|
530
|
+
everywhere. */
|
|
485
531
|
.screen {
|
|
486
532
|
--screen-gap: var(--sp-5); /* published: a .step cancels it to touch the bar it follows (step.css) */
|
|
487
533
|
max-width: var(--screen-w);
|
|
@@ -490,6 +536,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
490
536
|
display: grid;
|
|
491
537
|
gap: var(--screen-gap);
|
|
492
538
|
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 */
|
|
539
|
+
-webkit-user-select: var(--screen-select, none); user-select: var(--screen-select, none); -webkit-touch-callout: none;
|
|
493
540
|
}
|
|
494
541
|
.screen > * { min-width: 0; } /* content never widens the column past the viewport */
|
|
495
542
|
/* a screen that carries its own palette (data-palette on the element, not on <html>)
|
|
@@ -532,21 +579,18 @@ button { font: inherit; cursor: pointer; }
|
|
|
532
579
|
|
|
533
580
|
.t-muted { color: var(--on-surface-muted); }
|
|
534
581
|
.card .t-muted { color: var(--card-muted); } /* muted follows the card's ink, not the surface's */
|
|
535
|
-
.card--3 .t-muted
|
|
582
|
+
/* .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 */
|
|
536
583
|
.t-caps { text-transform: uppercase; }
|
|
537
584
|
.grow { flex: 1; min-width: 0; } /* the child that takes the remaining row space */
|
|
538
585
|
/* 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.
|
|
539
586
|
The 1px is the one the platform needs to keep it in the accessibility tree, not a length of the design. */
|
|
540
|
-
/* 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
|
|
541
|
-
callout — only what is typed into, and words marked selectable (`Text selectable` → .t-select: a code, an address).
|
|
542
|
-
Knob --screen-select: `text` = the web page's old way everywhere. */
|
|
543
|
-
.screen { -webkit-user-select: var(--screen-select, none); user-select: var(--screen-select, none); -webkit-touch-callout: none; }
|
|
544
587
|
.screen :is(input, textarea, [contenteditable="true"]), .screen .t-select { -webkit-user-select: text; user-select: text; -webkit-touch-callout: default; }
|
|
545
588
|
.t-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
|
|
546
589
|
|
|
547
|
-
/* ----
|
|
548
|
-
|
|
549
|
-
|
|
590
|
+
/* ---- page: words straight on the base, no card around them — a
|
|
591
|
+
heading, a paragraph, maybe a .cta-pack. Was .section until 2026-09-16
|
|
592
|
+
(Lh: the name said nothing about WHERE it lives), then .base-content
|
|
593
|
+
until 2026-09-23 (Lh: the content is a BOX; what's in it is .page).
|
|
550
594
|
In a .step it is the reading block and step.css adds the flex/scroll rule ---- */
|
|
551
595
|
.page {
|
|
552
596
|
display: grid;
|
|
@@ -580,14 +624,15 @@ button { font: inherit; cursor: pointer; }
|
|
|
580
624
|
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 */
|
|
581
625
|
}
|
|
582
626
|
.empty-state__action { margin-top: var(--sp-2); }
|
|
583
|
-
/* inside a card (a
|
|
627
|
+
/* inside a card (a card's own "nothing yet"): the card's ink */
|
|
584
628
|
.card .empty-state { color: var(--card-ink); padding-block: var(--sp-6); }
|
|
585
629
|
.card--3 .empty-state { color: inherit; }
|
|
586
630
|
|
|
587
631
|
/* ---- css/card.css — card boundary + list variants ---- */
|
|
588
632
|
/* ============================================================
|
|
589
633
|
cardds/card.css — the card boundary + list variants
|
|
590
|
-
One card = one boundary.
|
|
634
|
+
One card = one boundary. The base .card is tone 1; further tones
|
|
635
|
+
via .card--2/3/4/5 (role slots).
|
|
591
636
|
============================================================ */
|
|
592
637
|
|
|
593
638
|
.card {
|
|
@@ -612,14 +657,18 @@ button { font: inherit; cursor: pointer; }
|
|
|
612
657
|
.card > * { min-width: 0; }
|
|
613
658
|
|
|
614
659
|
.card--2 { --card-bg: var(--card-2-bg); --card-ink: var(--card-2-ink); --card-muted: var(--card-2-muted); }
|
|
615
|
-
.card--3 { --card-bg: var(--card-3-bg); --card-ink: var(--card-3-ink); }
|
|
616
|
-
.card--4 { --card-bg: var(--card-4-bg); --card-ink: var(--card-4-ink); --card-muted: var(--card-4-muted); }
|
|
617
|
-
.card--5 { --card-bg: var(--card-5-bg); --card-ink: var(--card-5-ink); --card-muted: var(--card-5-muted); }
|
|
618
|
-
|
|
619
660
|
/* dark/accent slot: accent-toned controls would vanish — the slot re-points
|
|
620
661
|
the accent tokens, so every accent consumer (btn--primary, chip--on,
|
|
621
662
|
check, toggle, ring, day…) inverts without per-component rules */
|
|
622
|
-
.card--3 { --accent: var(--card-3-ink); --on-accent: var(--card-3-bg); }
|
|
663
|
+
.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); }
|
|
664
|
+
.card--4 { --card-bg: var(--card-4-bg); --card-ink: var(--card-4-ink); --card-muted: var(--card-4-muted); }
|
|
665
|
+
.card--5 { --card-bg: var(--card-5-bg); --card-ink: var(--card-5-ink); --card-muted: var(--card-5-muted); }
|
|
666
|
+
/* the dark slot's captions/hints/footnotes: --card-muted is tuned for a light card, so these dim the full ink
|
|
667
|
+
instead (one rule, matching specificity to what it replaces — .card--3 <space> <one class>) — merged from
|
|
668
|
+
base.css/choice.css/media.css/numbers.css/journey.css/people.css (2026-09-27 review). lists.css's .legend
|
|
669
|
+
differs (opacity 0.8, not --dim) and stays there. */
|
|
670
|
+
.card--3 :is(.t-muted, .calendar__wd, .composer__hint, .quote__by, .ring__label, .band__foot, .picker__unit,
|
|
671
|
+
.route__place, .note-row, .avatar-pick__role) { color: inherit; opacity: var(--dim); }
|
|
623
672
|
|
|
624
673
|
/* header row: title left, floating action right — centered, since the
|
|
625
674
|
trailing control (chip/badge/icon-btn/link) is almost always taller than
|
|
@@ -722,7 +771,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
722
771
|
linear-gradient(to top left, transparent calc(50% - 1px), currentColor calc(50% - 1px), currentColor calc(50% + 1px), transparent calc(50% + 1px));
|
|
723
772
|
border: var(--border-w) solid currentColor;
|
|
724
773
|
border-radius: var(--r-media);
|
|
725
|
-
opacity:
|
|
774
|
+
opacity: var(--off);
|
|
726
775
|
min-height: 10rem;
|
|
727
776
|
}
|
|
728
777
|
.ph--sm { min-height: 7.5rem; }
|
|
@@ -748,8 +797,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
748
797
|
one is there, and the screen's gap), or twice the room below it (the
|
|
749
798
|
bar's reserve) — whichever is less. --_lead is the title bar's height
|
|
750
799
|
only on a screen that HAS one: without a .topbar/.appbar before it the
|
|
751
|
-
block keeps that room (a
|
|
752
|
-
under it, need it on a short phone; Lh, 2026-09-11). */
|
|
800
|
+
block keeps that room (a card and its pager under it needs it on a short phone; Lh, 2026-09-11). */
|
|
753
801
|
--_lead: 0rem;
|
|
754
802
|
max-height: calc(2 * min(var(--screen-centre) - var(--_lead) - var(--screen-gap), 100% - var(--screen-centre) - var(--bar-reserve)));
|
|
755
803
|
overflow-y: auto;
|
|
@@ -760,9 +808,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
760
808
|
|
|
761
809
|
/* ---- centred block: the same place, for one thing to read that is not a
|
|
762
810
|
lone card — an empty state, a card with its quiet second button and the
|
|
763
|
-
pager under it
|
|
764
|
-
edge and keeps the screen's inset as padding,
|
|
765
|
-
overhang and clip at the screen's edges (correct — the fold visual), and
|
|
811
|
+
pager under it. The block runs edge to
|
|
812
|
+
edge and keeps the screen's inset as padding, and
|
|
766
813
|
its children stack at the screen's own rhythm (Lh, 2026-09-11):
|
|
767
814
|
<div class="screen screen--fill"><header class="topbar">…</header>
|
|
768
815
|
<div class="screen__centre"><article class="card">…</article><button class="btn btn--quiet btn--block">…</button></div>
|
|
@@ -780,9 +827,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
780
827
|
likes when not (Lh, 2026-09-12) ----
|
|
781
828
|
The member sees one content area — whatever is between the top bar and the
|
|
782
829
|
bar — and reads it as a whole page: a short card sits at its middle, a long
|
|
783
|
-
one simply makes the page longer, runs under the bar (
|
|
784
|
-
|
|
785
|
-
|
|
830
|
+
one simply makes the page longer, runs under the bar (a sticky last row,
|
|
831
|
+
actions.css) and scrolls up like any page — nothing is capped, nothing
|
|
832
|
+
shrinks, no scroll box inside a box.
|
|
786
833
|
So on a .screen that is NOT --fill the group is in flow: the column is at
|
|
787
834
|
least one phone tall, the group takes the room after the top bar and
|
|
788
835
|
centres its children in it. The screen's own inset stays the inset.
|
|
@@ -800,9 +847,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
800
847
|
the React adapter's slideTo() sets data-slide on :root, starts the transition
|
|
801
848
|
and updates inside it; a browser without them (the floor: Chrome 105 / iOS 16)
|
|
802
849
|
simply switches. Only the card is named; the rest of the page must not
|
|
803
|
-
cross-fade.
|
|
804
|
-
|
|
805
|
-
|
|
850
|
+
cross-fade. The name is set by slideTo() on the moving element for the move only (2026-09-22): a static name on
|
|
851
|
+
every walk's current page made two walks on one page — the gallery — share it, and the browser aborts a
|
|
852
|
+
transition with a duplicate name. */
|
|
806
853
|
|
|
807
854
|
/* ---- pages: the cards of a walk, all one height ----
|
|
808
855
|
A sequence the member walks (an introduction) reads steadier when every card
|
|
@@ -835,14 +882,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
835
882
|
animation-timing-function: ease-out;
|
|
836
883
|
mix-blend-mode: normal;
|
|
837
884
|
}
|
|
838
|
-
:root[data-slide="next"]::view-transition-old(centre-card-out) { animation-name: centre-card-out-left; }
|
|
839
|
-
:root[data-slide="next"]::view-transition-new(centre-card-in) { animation-name: centre-card-in-right; }
|
|
840
|
-
:root[data-slide="back"]::view-transition-old(centre-card-out) { animation-name: centre-card-out-right; }
|
|
841
|
-
:root[data-slide="back"]::view-transition-new(centre-card-in) { animation-name: centre-card-in-left; }
|
|
842
|
-
:root[data-slide="next"]::view-transition-old(screen-out) { animation-name: centre-card-out-left; }
|
|
843
|
-
:root[data-slide="next"]::view-transition-new(screen-in) { animation-name: centre-card-in-right; }
|
|
844
|
-
:root[data-slide="back"]::view-transition-old(screen-out) { animation-name: centre-card-out-right; }
|
|
845
|
-
:root[data-slide="back"]::view-transition-new(screen-in) { animation-name: centre-card-in-left; }
|
|
885
|
+
:root[data-slide="next"]::view-transition-old(centre-card-out), :root[data-slide="next"]::view-transition-old(screen-out) { animation-name: centre-card-out-left; }
|
|
886
|
+
:root[data-slide="next"]::view-transition-new(centre-card-in), :root[data-slide="next"]::view-transition-new(screen-in) { animation-name: centre-card-in-right; }
|
|
887
|
+
:root[data-slide="back"]::view-transition-old(centre-card-out), :root[data-slide="back"]::view-transition-old(screen-out) { animation-name: centre-card-out-right; }
|
|
888
|
+
:root[data-slide="back"]::view-transition-new(centre-card-in), :root[data-slide="back"]::view-transition-new(screen-in) { animation-name: centre-card-in-left; }
|
|
846
889
|
@keyframes centre-card-out-left { to { translate: -100% 0; opacity: 0; } }
|
|
847
890
|
@keyframes centre-card-out-right { to { translate: 100% 0; opacity: 0; } }
|
|
848
891
|
@keyframes centre-card-in-right { from { translate: 100% 0; opacity: 0; } }
|
|
@@ -938,7 +981,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
938
981
|
bottom, the headroom above is the base's (block below).
|
|
939
982
|
Direct children only, five at most (slots are :nth-child below):
|
|
940
983
|
first = the back of the stack, last = the front. README "Sheet stack
|
|
941
|
-
markup".
|
|
984
|
+
markup". The SheetStack component opens and closes a --tap stack; without it the
|
|
942
985
|
stack still renders, it just won't open.
|
|
943
986
|
============================================================ */
|
|
944
987
|
|
|
@@ -951,14 +994,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
951
994
|
track is being sized), so a long sheet grew the track past the screen and
|
|
952
995
|
every sheet's 100% then resolved against that — the bottom of every open
|
|
953
996
|
sheet sat below the screen, unreachable (timebank home, 2026-09-13). */
|
|
954
|
-
.sheet-stack { display: grid; grid-template-rows: minmax(0, 1fr); --stack-step: 5.5rem; }
|
|
955
|
-
|
|
956
997
|
/* the stack breaks out of the screen inset, edge to edge, always: the peek
|
|
957
998
|
strips and the opened sheet span the screen's full width whatever the
|
|
958
999
|
viewport (it used to bleed only under a 27em media query — a stack in a
|
|
959
1000
|
phone frame on a desktop kept the inset, and read as a narrower card;
|
|
960
|
-
Lh, 2026-09-11)
|
|
961
|
-
|
|
1001
|
+
Lh, 2026-09-11). --_n: how many sheets the stack holds, read from the DOM
|
|
1002
|
+
by :has below so the closed geometry needs no script and no per-stack
|
|
1003
|
+
declaration. */
|
|
1004
|
+
.sheet-stack { display: grid; grid-template-rows: minmax(0, 1fr); --stack-step: 5.5rem; margin-inline: calc(-1 * var(--screen-pad)); --_n: 1; }
|
|
962
1005
|
.sheet-stack > .card {
|
|
963
1006
|
grid-area: 1 / 1; /* every card shares the stack's cell; later ones offset down */
|
|
964
1007
|
border-bottom-left-radius: 0;
|
|
@@ -975,9 +1018,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
975
1018
|
.sheet-stack > .card:not(:has(~ .card)) {
|
|
976
1019
|
align-self: stretch; /* the front card fills the remaining stack height */
|
|
977
1020
|
}
|
|
978
|
-
/* --_n: how many sheets the stack holds, read from the DOM by :has so the
|
|
979
|
-
closed geometry needs no script and no per-stack declaration */
|
|
980
|
-
.sheet-stack { --_n: 1; }
|
|
981
1021
|
.sheet-stack:has(> .card:nth-child(2)) { --_n: 2; }
|
|
982
1022
|
.sheet-stack:has(> .card:nth-child(3)) { --_n: 3; }
|
|
983
1023
|
.sheet-stack:has(> .card:nth-child(4)) { --_n: 4; }
|
|
@@ -1024,7 +1064,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1024
1064
|
/* fanned stack: a toggle opens the stack wider so every peek shows more */
|
|
1025
1065
|
.sheet-stack--fanned { --stack-step: 9.5rem; }
|
|
1026
1066
|
|
|
1027
|
-
.sheet-stack > .card:has(~ .card) { cursor: pointer; }
|
|
1028
1067
|
/* a peeked card in a plain stack hides its own body. A --tap stack does
|
|
1029
1068
|
NOT: there, every card is full height and full content always, and a
|
|
1030
1069
|
peek is simply the strip of it the next card doesn't cover — the way a
|
|
@@ -1070,9 +1109,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
1070
1109
|
by n steps (clipped by .screen--fill), so translating it up by exactly
|
|
1071
1110
|
that much lands it filling the stack precisely. */
|
|
1072
1111
|
overflow: clip;
|
|
1073
|
-
transition: translate
|
|
1112
|
+
transition: translate var(--motion-sheet) ease; /* the only animated property here */
|
|
1074
1113
|
transition-delay: calc(var(--stagger, 0) * 70ms); /* 0 for everything
|
|
1075
1114
|
except a staggered is-before card, set in JS as it tucks or un-tucks */
|
|
1115
|
+
--_back-size: var(--stack-back-size, calc(var(--tap-base) * 2 / 3)); /* read once, shared by the back button and the head's padding below */
|
|
1076
1116
|
}
|
|
1077
1117
|
.sheet-stack--tap > .card.is-open {
|
|
1078
1118
|
/* up by its own resting offset — pure movement, nothing resizes */
|
|
@@ -1094,9 +1134,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1094
1134
|
overscroll-behavior: contain;
|
|
1095
1135
|
}
|
|
1096
1136
|
/* a scrolling sheet shows no scrollbar (a desktop with a mouse would paint one
|
|
1097
|
-
down the card's edge — Lh 2026-09-15); it still scrolls, like the page itself
|
|
1098
|
-
|
|
1099
|
-
.sheet-stack--tap > .card::-webkit-scrollbar { display: none; }
|
|
1137
|
+
down the card's edge — Lh 2026-09-15); it still scrolls, like the page itself
|
|
1138
|
+
(the hiding rule is base.css's, shared with every other scrolling track) */
|
|
1100
1139
|
.sheet-stack--tap > .card.is-before { translate: 0 var(--stack-step); }
|
|
1101
1140
|
.sheet-stack--tap > .card.is-after { translate: 0 100vh; }
|
|
1102
1141
|
|
|
@@ -1115,7 +1154,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1115
1154
|
lands flush at the top. is-before tucks one step below its resting place.
|
|
1116
1155
|
============================================================ */
|
|
1117
1156
|
.sheet-stack--closed { --_headroom: calc(100% - var(--_n) * var(--stack-step)); }
|
|
1118
|
-
.sheet-stack--closed > .card { translate: 0 var(--_headroom);
|
|
1157
|
+
.sheet-stack--closed > .card { translate: 0 var(--_headroom); }
|
|
1119
1158
|
.sheet-stack--closed > .card.is-before { translate: 0 calc(var(--_headroom) + var(--stack-step)); }
|
|
1120
1159
|
.sheet-stack--closed > .card.is-open { translate: 0 calc(-1 * var(--slot, 0) * var(--stack-step)); }
|
|
1121
1160
|
.sheet-stack--closed > .card.is-after { translate: 0 100vh; }
|
|
@@ -1147,10 +1186,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1147
1186
|
.sheet-stack--tap .card__head {
|
|
1148
1187
|
position: relative;
|
|
1149
1188
|
padding-left: 0;
|
|
1150
|
-
transition: padding-left
|
|
1151
|
-
}
|
|
1152
|
-
.sheet-stack--tap > .card.is-open > .card__head {
|
|
1153
|
-
padding-left: calc(var(--tap-sm) + var(--sp-3));
|
|
1189
|
+
transition: padding-left var(--motion-sheet) ease;
|
|
1154
1190
|
}
|
|
1155
1191
|
.sheet-stack--tap .sheet-stack__back {
|
|
1156
1192
|
position: absolute;
|
|
@@ -1159,7 +1195,13 @@ button { font: inherit; cursor: pointer; }
|
|
|
1159
1195
|
translate: 0 -50%;
|
|
1160
1196
|
opacity: 0;
|
|
1161
1197
|
visibility: hidden;
|
|
1162
|
-
transition: opacity
|
|
1198
|
+
transition: opacity var(--motion-sheet) ease, visibility var(--motion-sheet);
|
|
1199
|
+
/* SMALL (see "the back button, SMALL" below) */
|
|
1200
|
+
width: var(--_back-size);
|
|
1201
|
+
height: var(--_back-size);
|
|
1202
|
+
min-width: 0;
|
|
1203
|
+
min-height: 0;
|
|
1204
|
+
padding: 0;
|
|
1163
1205
|
}
|
|
1164
1206
|
.sheet-stack--tap > .card.is-open .sheet-stack__back {
|
|
1165
1207
|
opacity: 1;
|
|
@@ -1218,28 +1260,21 @@ button { font: inherit; cursor: pointer; }
|
|
|
1218
1260
|
/* ---- the back button, SMALL (Lh 2026-09-23: "give the sheet head more room" — and then "a glyph alone does not read as
|
|
1219
1261
|
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
|
|
1220
1262
|
(was 56px). Its tap target stays 44px: an invisible ::after around the disc. ---- */
|
|
1221
|
-
.sheet-stack--tap > .card.is-open > .card__head { padding-left: var(--stack-back-room, calc(var(--
|
|
1222
|
-
.sheet-stack--tap .sheet-stack__back {
|
|
1223
|
-
width: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
|
|
1224
|
-
height: var(--stack-back-size, calc(var(--tap-base) * 2 / 3));
|
|
1225
|
-
min-width: 0;
|
|
1226
|
-
min-height: 0;
|
|
1227
|
-
padding: 0;
|
|
1228
|
-
}
|
|
1263
|
+
.sheet-stack--tap > .card.is-open > .card__head { padding-left: var(--stack-back-room, calc(var(--_back-size) + var(--sp-2))); }
|
|
1229
1264
|
.sheet-stack--tap .sheet-stack__back > .icon { width: var(--stack-back-icon, calc(var(--tap-base) * 13 / 24)); height: var(--stack-back-icon, calc(var(--tap-base) * 13 / 24)); flex: none; } /* 26: the chevron fills the small disc (Lh 2026-09-23) */
|
|
1230
|
-
.sheet-stack--tap .sheet-stack__back::after { content: ""; position: absolute; inset: calc((var(--
|
|
1265
|
+
.sheet-stack--tap .sheet-stack__back::after { content: ""; position: absolute; inset: calc((var(--_back-size) - var(--tap-sm)) / 2); }
|
|
1231
1266
|
|
|
1232
1267
|
/* the open sheet's title folds it (SheetStack.tsx): it says so to a pointer */
|
|
1233
1268
|
.sheet-stack--tap > .card.is-open > .card__head > :is(h1, h2, h3) { cursor: pointer; }
|
|
1234
1269
|
/* 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),
|
|
1235
1270
|
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) */
|
|
1236
|
-
.sheet-stack--tap > .card, .sheet-stack > .card > .card__head { -webkit-user-select: none; user-select: none; -webkit-
|
|
1271
|
+
.sheet-stack--tap > .card, .sheet-stack > .card > .card__head { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
|
|
1237
1272
|
.sheet-stack--tap > .card.is-open > :not(.card__head) { -webkit-user-select: var(--screen-select, none); user-select: var(--screen-select, none); } /* the open sheet reads as the rest of the screen: not selectable (Lh 2026-09-25; it was text) */
|
|
1238
1273
|
|
|
1239
1274
|
/* ---- css/sheet.css — sheet single: .sheet — one card as a bottom sheet, peek / half / full ---- */
|
|
1240
1275
|
/* ============================================================
|
|
1241
1276
|
cardds/sheet.css — sheet single: one card as a bottom sheet.
|
|
1242
|
-
|
|
1277
|
+
Five states: .sheet--peek · .sheet--half · .sheet--3q · .sheet--full · .sheet--away
|
|
1243
1278
|
|
|
1244
1279
|
A sheet is rigid material, exactly like a sheet in .sheet-stack--tap
|
|
1245
1280
|
(stack.css): it is ALWAYS the full height of the box that holds it —
|
|
@@ -1280,8 +1315,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1280
1315
|
the body. The handle and the head stay where they are at every
|
|
1281
1316
|
state; the body is what scrolls — a sheet is a card whose head is
|
|
1282
1317
|
pinned, not a scrolling column with a title somewhere in it.
|
|
1283
|
-
|
|
1284
|
-
raise; without it every state still renders.
|
|
1318
|
+
The Sheet component adds the handle's click / drag / keyboard and the
|
|
1319
|
+
focus raise; without it every state still renders.
|
|
1285
1320
|
============================================================ */
|
|
1286
1321
|
|
|
1287
1322
|
.sheet {
|
|
@@ -1309,7 +1344,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1309
1344
|
position: absolute;
|
|
1310
1345
|
inset: 0;
|
|
1311
1346
|
top: var(--_top, 0); /* a stage keeps its top bar's line free — see .sheet-stage */
|
|
1312
|
-
translate: 0 calc(var(--_y) + var(--_drag, 0rem)); /* --_drag:
|
|
1347
|
+
translate: 0 calc(var(--_y) + var(--_drag, 0rem)); /* --_drag: the Sheet component, only while the handle is held */
|
|
1313
1348
|
transition: translate var(--motion-sheet) ease;
|
|
1314
1349
|
|
|
1315
1350
|
/* handle · head · body: a column, the body takes what is left and scrolls
|
|
@@ -1320,6 +1355,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1320
1355
|
border-radius: var(--r-card) var(--r-card) 0 0;
|
|
1321
1356
|
border-bottom: 0;
|
|
1322
1357
|
box-shadow: var(--sheet-shadow, var(--shadow-sheet));
|
|
1358
|
+
--_foot: calc(var(--card-gap) + var(--btn-h) + var(--card-pad)); /* the foot strip's height (below) — merged here, one bare .sheet rule */
|
|
1323
1359
|
}
|
|
1324
1360
|
.sheet--full { --_y: 0%; --_cover: 0rem; }
|
|
1325
1361
|
.sheet--half { --_y: calc(var(--sheet-half) * 100%); --_cover: calc(var(--sheet-half) * var(--_box)); }
|
|
@@ -1385,9 +1421,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1385
1421
|
takes no taps, its buttons do; the strip behind them is the sheet's ground, --_foot tall, and the body
|
|
1386
1422
|
stops that far above the sheet's edge. At peek there is only the handle and the title: the foot is gone.
|
|
1387
1423
|
One row, ≤ 2 buttons. <div class="card__foot"><button class="btn btn--primary btn--block">…</button></div> */
|
|
1388
|
-
.sheet { --_foot: calc(var(--card-gap) + var(--btn-h) + var(--card-pad)); }
|
|
1389
1424
|
/* …and the link row under the button, when there is one (its line + the gap over it) */
|
|
1390
|
-
.sheet:has(> .card__foot--link) { --_foot: calc(var(--card-gap) + var(--btn-h) + var(--sp-1) + calc(var(--tap-base) * 7 / 12) + var(--card-pad)); }
|
|
1425
|
+
.sheet:has(> .card__foot--link) { --_foot: calc(var(--card-gap) + var(--btn-h) + var(--sp-1) + var(--link-sm-min-h, calc(var(--tap-base) * 7 / 12)) + var(--card-pad)); }
|
|
1391
1426
|
.sheet > .card__foot {
|
|
1392
1427
|
position: absolute; inset: calc(-1 * var(--border-w)) 0 0; z-index: 1; /* the sheet's border box: the same height the sheet's own % reads */
|
|
1393
1428
|
align-items: end;
|
|
@@ -1413,7 +1448,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1413
1448
|
/* grab handle — first child of the sheet. Drawn as a small bar, but it is
|
|
1414
1449
|
the thumb's grab area for the whole strip above the title: a pseudo widens
|
|
1415
1450
|
its hit box to --tap tall and wider than the bar without changing the
|
|
1416
|
-
picture. touch-action: none hands a touch drag to
|
|
1451
|
+
picture. touch-action: none hands a touch drag to the Sheet component instead of to
|
|
1417
1452
|
the scroller behind it. */
|
|
1418
1453
|
.sheet__handle {
|
|
1419
1454
|
position: relative;
|
|
@@ -1487,7 +1522,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1487
1522
|
.screen > .sheet-stage { margin-inline: calc(-1 * var(--screen-pad)); width: auto; } /* auto: 100% would keep the inset width */
|
|
1488
1523
|
/* a stage laid OVER its screen (a Modal a link opened, Lh 2026-09-23): the page shows through, the stage covers it and its bar */
|
|
1489
1524
|
.sheet-stage--over, .screen > .sheet-stage--over {
|
|
1490
|
-
position: absolute; inset: 0; z-index:
|
|
1525
|
+
position: absolute; inset: 0; z-index: var(--z-top);
|
|
1491
1526
|
width: auto; height: auto; max-width: none; margin: 0;
|
|
1492
1527
|
background: transparent;
|
|
1493
1528
|
--_top: 0rem;
|
|
@@ -1544,7 +1579,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1544
1579
|
translate: -50% -50%;
|
|
1545
1580
|
border-radius: 50%;
|
|
1546
1581
|
border: var(--border-w) solid var(--map-ring-border, var(--accent));
|
|
1547
|
-
background: var(--map-ring-bg,
|
|
1582
|
+
background: var(--map-ring-bg, var(--accent-soft));
|
|
1548
1583
|
box-shadow: var(--map-ring-shadow, var(--shadow-float));
|
|
1549
1584
|
pointer-events: none;
|
|
1550
1585
|
z-index: 1; /* above the engine's panes (Leaflet's tiles sit at 200, but inside their own stacking context) */
|
|
@@ -1578,9 +1613,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1578
1613
|
z-index: 1;
|
|
1579
1614
|
}
|
|
1580
1615
|
|
|
1581
|
-
/* raised: the sheet floats above a footnote strip on the scrim. Its bottom
|
|
1582
|
-
edge shows, so it gets its corners and border back; full still stops at
|
|
1583
|
-
the top bar's line, because the top edge never moved. */
|
|
1584
1616
|
/* EVERY sheet has one shape (Lh, 2026-09-21): the handle and the head stay at the top and never scroll · the
|
|
1585
1617
|
foot's button stands at the bottom, and ABOVE the keyboard when there is one · everything between them
|
|
1586
1618
|
scrolls, as far as it needs to. In a .step the keyboard's room is the step's (--kb, step.css); on a stage
|
|
@@ -1619,9 +1651,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1619
1651
|
bottom of the body, each its title line. Every step has a thin rule on top. A step opens and folds as a 1fr ↔ 0fr grid row, so the pile rises and
|
|
1620
1652
|
falls — no height is written. Nothing fades: done and next read at full ink. */
|
|
1621
1653
|
.sheet__body:has(> .sheet-steps) { display: flex; flex-direction: column; }
|
|
1622
|
-
.sheet-steps { flex-shrink: 0; min-height: calc(100% + var(--kb, 0rem)); /* the keyboard's height more: the pile of next steps stays under the keyboard, it never rises with it */ display: flex; flex-direction: column; touch-action: pan-y; }
|
|
1654
|
+
.sheet-steps { flex-shrink: 0; min-height: calc(100% + var(--kb, 0rem)); /* the keyboard's height more: the pile of next steps stays under the keyboard, it never rises with it */ display: flex; flex-direction: column; touch-action: pan-y; position: relative; }
|
|
1623
1655
|
.sheet-steps__holder { position: absolute; top: 0; left: 0; width: 1rem; height: 1rem; padding: 0; border: 0; opacity: 0; pointer-events: none; font-size: 1rem; } /* holds the cursor (and the phone's keyboard) while a step folds; font 1rem = the browser's own size, so iOS does not zoom to it */
|
|
1624
|
-
.sheet-steps { position: relative; }
|
|
1625
1656
|
.sheet-steps__track { list-style: none; margin: 0; padding: 0; flex: 1; display: flex; flex-direction: column; }
|
|
1626
1657
|
|
|
1627
1658
|
.sheet-step {
|
|
@@ -1629,6 +1660,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1629
1660
|
display: grid; grid-template-rows: auto 0fr; align-content: start; /* mid-fold the rows sum to less than the box: the spare room stays BELOW, never spread into the title row (Lh: it looked like a spring) */
|
|
1630
1661
|
flex: 0 0 auto;
|
|
1631
1662
|
transition: grid-template-rows var(--motion-sheet) ease, flex-grow var(--motion-sheet) ease;
|
|
1663
|
+
--_row-h: var(--sheet-steps-row-h, calc(var(--tap-base) * 2 / 3)); /* the bar's own floor, read once (__head and __go below share it) */
|
|
1632
1664
|
}
|
|
1633
1665
|
/* the open step GROWS into the room left, so the steps not done yet wait in a pile at the bottom — and a move is an ACCORDION:
|
|
1634
1666
|
in one beat the step that was open folds while the new one unfolds (flex-grow and the 1fr row both ease), the piles slide
|
|
@@ -1636,26 +1668,25 @@ button { font: inherit; cursor: pointer; }
|
|
|
1636
1668
|
.sheet-step--now { grid-template-rows: auto 1fr; flex-grow: 1; }
|
|
1637
1669
|
.sheet-step + .sheet-step { margin-top: var(--sheet-steps-gap, var(--sp-1)); } /* the folded bars read as a pile, a little ground between them (Lh 2026-09-25) */
|
|
1638
1670
|
.sheet-step__head { /* every step's title line is a quiet BAR (a tint of the ink, not the brand — a brand bar read as a button, Lh 2026-09-25), the same height open or folded — opening shows the white room under it, never a taller bar (Lh 2026-09-25; was a thin rule on top) */
|
|
1639
|
-
display: flex; align-items: center; gap: var(--sp-3); min-height: var(--
|
|
1671
|
+
display: flex; align-items: center; gap: var(--sp-3); min-height: var(--_row-h); /* THIN: 32px, a summary line, not a button (Lh 2026-09-25) — the tap stays 44, see __go */
|
|
1640
1672
|
padding-inline: var(--sheet-step-bar-pad-x, var(--sp-3)); border-radius: var(--sheet-step-bar-radius, var(--r-chip));
|
|
1641
1673
|
background: var(--sheet-step-bar-bg, var(--ink-tint)); color: var(--sheet-step-bar-ink, inherit);
|
|
1642
|
-
border-top: var(--sheet-step-border-w,
|
|
1674
|
+
border-top: var(--sheet-step-border-w, 0rem) solid var(--sheet-step-border, currentColor); /* a rule instead of the bar: set this, the bar's bg transparent, radius 0 */
|
|
1643
1675
|
}
|
|
1644
|
-
.sheet-step__summary { color: inherit; opacity: 0.8; }
|
|
1645
1676
|
.sheet-step__title { font: var(--sheet-step-title-font, var(--type-label)); margin: 0; flex: 0 1 auto; min-width: 0; outline: none; /* the label size, not a body title: a pile of rows must stay small (Lh 2026-09-24) */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1646
1677
|
.sheet-step__num { /* `numbered`: the step's number in a disc BEFORE its title (Lh 2026-09-25) — a white disc on the bar, the number in the ink */
|
|
1647
1678
|
flex: none; display: grid; place-items: center; inline-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2)); block-size: var(--sheet-step-num-size, calc(var(--tap-base) / 2));
|
|
1648
1679
|
border-radius: 50%; font: var(--sheet-step-num-font, var(--type-caption)); font-variant-numeric: tabular-nums;
|
|
1649
1680
|
background: var(--sheet-step-num-bg, var(--card-bg)); color: var(--sheet-step-num-ink, inherit);
|
|
1650
1681
|
}
|
|
1651
|
-
.sheet-step__summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1682
|
+
.sheet-step__summary { color: inherit; opacity: 0.8; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1652
1683
|
.sheet-step__check { margin-inline-start: auto; }
|
|
1653
1684
|
.sheet-step__body { min-height: 0; overflow: clip; overflow-clip-margin: var(--sp-1); }
|
|
1654
1685
|
.sheet-step__inner { display: grid; gap: var(--card-gap); padding-block: var(--card-gap); }
|
|
1655
1686
|
.sheet-step__inner > * { min-width: 0; }
|
|
1656
1687
|
.sheet-step__inner > .link { justify-self: center; } /* a link of its own row in a step (skip, "change number", "set PIN again"): centred under the step, as the skip (Lh 2026-09-25) */
|
|
1657
1688
|
.sheet-step:not(.sheet-step--now) > .sheet-step__body { visibility: hidden; transition: visibility 0s linear var(--motion-sheet); }
|
|
1658
|
-
.sheet-step__go { position: absolute; inset: min(
|
|
1689
|
+
.sheet-step__go { position: absolute; inset: min(0rem, (var(--_row-h) - var(--tap-sm)) / 2) 0 auto; min-height: max(100%, var(--tap-sm)); /* a thin bar still takes a 44px tap */ margin: 0; cursor: pointer; }
|
|
1659
1690
|
/* the last step's finish, when the app gives one: under the steps */
|
|
1660
1691
|
.sheet-steps__finish { display: grid; padding-top: var(--card-gap); }
|
|
1661
1692
|
@media (prefers-reduced-motion: reduce) { .sheet-step { transition: none; } }
|
|
@@ -1724,9 +1755,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1724
1755
|
more air and a short one less; a screen with a bar keeps the bar's own room */
|
|
1725
1756
|
.screen > .step:first-child, .screen--lead > .step { --_lead: var(--screen-lead, 15cqh); } /* top="lead" says it; a Step first says it too */
|
|
1726
1757
|
|
|
1727
|
-
/* a walk (the dots float on its foot): the reading block runs right to the bar's top edge — the reserve's breathing gap (--sp-6)
|
|
1728
|
-
is given back, the dots keep the words clear of the bar (Lh 2026-09-23) */
|
|
1729
|
-
|
|
1730
1758
|
/* LAYERS (Lh, 2026-09-20): the base, then the sheet over it, then the top bar. A bar is the BASE's — the
|
|
1731
1759
|
move of a page of words — so a step with a sheet has NO bar: the sheet finishes on its own .card__foot
|
|
1732
1760
|
(sheet.css), and a form lives whole in ONE sheet, never item by item on the base. That is how cardds's
|
|
@@ -1746,8 +1774,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1746
1774
|
--card-gap: var(--sp-4);
|
|
1747
1775
|
gap: var(--card-gap);
|
|
1748
1776
|
padding-block: 0; /* --_lead carries the top; the bar's room is the step's */
|
|
1749
|
-
align-content: start
|
|
1750
|
-
color: var(--on-surface);
|
|
1777
|
+
color: var(--on-surface); /* align-content: start is .page's own (base.css) — not restated here */
|
|
1751
1778
|
}
|
|
1752
1779
|
/* the reading block is the .page — or the .pages that holds several of them, a walk of words
|
|
1753
1780
|
(card.css). Behind a PEEKED sheet the words keep the strip's room under them, so whatever is down there
|
|
@@ -1755,23 +1782,22 @@ button { font: inherit; cursor: pointer; }
|
|
|
1755
1782
|
.step:has(> .sheet--peek) > :is(.page, .pages) { padding-bottom: var(--sheet-peek); }
|
|
1756
1783
|
.step > :is(.page, .pages) {
|
|
1757
1784
|
flex: 0 1 auto;
|
|
1758
|
-
}
|
|
1759
|
-
.step > :is(.page, .pages) {
|
|
1760
1785
|
min-height: 0;
|
|
1761
|
-
margin: var(--_lead, 0) var(--screen-pad) 0; /* the screen's gap, moved in here from above the step; the inset, taken back from the bleed */
|
|
1762
1786
|
overflow-y: auto;
|
|
1763
1787
|
overscroll-behavior: contain;
|
|
1764
1788
|
}
|
|
1789
|
+
/* a lone .page carries the margin a walk's .pages does not (its own .page children do, below) —
|
|
1790
|
+
the screen's gap, moved in here from above the step; the inset, taken back from the bleed */
|
|
1791
|
+
.step > .page { margin: var(--_lead, 0) var(--screen-pad) 0; }
|
|
1765
1792
|
|
|
1766
1793
|
/* 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
|
|
1767
1794
|
(the picture is never pinned). Each page starts at the screen's lead and ends with the same room under it — the lead as
|
|
1768
1795
|
padding above AND below — so the words begin near the middle and, when a big text size runs them long, scroll up clear.
|
|
1769
1796
|
The walk shows no scrollbar. */
|
|
1770
|
-
.step > .pages {
|
|
1771
|
-
.step > .pages > .page { padding-inline: var(--screen-pad); } /* the words keep the inset INSIDE it */
|
|
1772
|
-
.step > .pages > .page { margin-top: var(--_lead, 0rem); } /* the lead is each PAGE's margin (15% with no top, the screen's gap under a bar): the words start there and scroll up to the box's edge */
|
|
1773
|
-
.step > .pages::-webkit-scrollbar { display: none; }
|
|
1797
|
+
.step > .pages { touch-action: pan-y; } /* pan-y: the walk scrolls itself up and down; a sideways swipe is the Walkthrough's (it turns the page) */ /* the scrolling box is the whole content: from the step's top edge, edge to edge — no margin, the default (Lh 2026-09-23) */
|
|
1774
1798
|
.step > .pages > .page {
|
|
1799
|
+
padding-inline: var(--screen-pad); /* the words keep the inset INSIDE it */
|
|
1800
|
+
margin-top: var(--_lead, 0rem); /* the lead is each PAGE's margin (15% with no top, the screen's gap under a bar): the words start there and scroll up to the box's edge */
|
|
1775
1801
|
padding-bottom: var(--bottom-h, 0rem); /* exactly the bottom layer (measured by Screen): a page scrolls ONLY when the layer would cover its words, and then just far enough to clear them — at 100% nothing scrolls (Lh 2026-09-23) */
|
|
1776
1802
|
}
|
|
1777
1803
|
|
|
@@ -1876,9 +1902,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1876
1902
|
display: inline-flex;
|
|
1877
1903
|
align-items: center;
|
|
1878
1904
|
gap: var(--add-row-gap, var(--sp-2));
|
|
1879
|
-
border: 0;
|
|
1880
|
-
background: none;
|
|
1881
|
-
color: inherit;
|
|
1882
1905
|
font: var(--add-row-font, var(--type-label));
|
|
1883
1906
|
font-size: var(--add-row-font-size, var(--fs-body));
|
|
1884
1907
|
padding: var(--add-row-pad-y, var(--sp-2)) var(--add-row-pad-x, 0);
|
|
@@ -1944,7 +1967,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1944
1967
|
.route__point--end .route__code::after { content: none; }
|
|
1945
1968
|
.route__time { font: var(--route-time-font, var(--type-body)); font-variant-numeric: tabular-nums; }
|
|
1946
1969
|
.route__place { font: var(--route-place-font, var(--type-caption)); color: var(--route-place-ink, var(--card-muted)); }
|
|
1947
|
-
.card--3 .route__place { color: inherit; opacity: 0.7; }
|
|
1948
1970
|
|
|
1949
1971
|
.route__via { grid-column: 2; grid-row: 2; align-self: center; display: flex; align-items: center; opacity: 0.65; font-size: var(--route-via-font-size, var(--fs-body)); }
|
|
1950
1972
|
|
|
@@ -1958,7 +1980,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1958
1980
|
background: var(--tile-badge-bg, var(--card-3-bg));
|
|
1959
1981
|
color: var(--tile-badge-ink, var(--card-3-ink));
|
|
1960
1982
|
}
|
|
1961
|
-
.tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity:
|
|
1983
|
+
.tile-badge__label { font: var(--tile-badge-label-font, var(--type-caption)); opacity: var(--dim); }
|
|
1962
1984
|
.tile-badge__value { font: 800 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
|
|
1963
1985
|
|
|
1964
1986
|
/* plain: no fill — a date box beside a title (Jun / 18) */
|
|
@@ -1976,7 +1998,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
1976
1998
|
font: var(--note-row-font, var(--type-caption));
|
|
1977
1999
|
color: var(--note-row-ink, var(--card-muted));
|
|
1978
2000
|
}
|
|
1979
|
-
.card--3 .note-row { color: inherit; opacity: 0.7; }
|
|
1980
2001
|
|
|
1981
2002
|
/* ---- css/actions.css — topbar, chips, buttons, CTA pack ---- */
|
|
1982
2003
|
/* ============================================================
|
|
@@ -2036,12 +2057,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
2036
2057
|
max-width: var(--screen-w);
|
|
2037
2058
|
margin-inline: auto;
|
|
2038
2059
|
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) */
|
|
2039
|
-
z-index:
|
|
2060
|
+
z-index: var(--z-top); /* topmost — card animations never cover it */
|
|
2040
2061
|
background: var(--appbar-bg, var(--surface)); /* solid: sliding cards never show through */
|
|
2041
2062
|
}
|
|
2042
2063
|
/* fixed, so it covers the top of the screen it sits in — reserve its height
|
|
2043
|
-
there, from the same token
|
|
2044
|
-
|
|
2064
|
+
there, from the same header-h token. The breathing under it is published:
|
|
2065
|
+
a .step cancels it to touch the bar. */
|
|
2045
2066
|
.screen:has(> .appbar) { --header-gap: var(--sp-2); padding-top: calc(var(--header-h) + var(--header-gap)); }
|
|
2046
2067
|
.appbar__brand { flex: 1; display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
|
|
2047
2068
|
.appbar__text { display: grid; min-width: 0; flex: 1; } /* pushes trailing button to the right edge */
|
|
@@ -2049,11 +2070,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
2049
2070
|
.appbar__who {
|
|
2050
2071
|
flex: 1; min-width: 0; min-height: var(--tap);
|
|
2051
2072
|
display: flex; align-items: center; gap: var(--sp-2);
|
|
2052
|
-
|
|
2073
|
+
margin: 0; text-align: start;
|
|
2053
2074
|
border-radius: var(--appbar-who-radius, var(--r-chip));
|
|
2054
|
-
-webkit-tap-highlight-color: transparent;
|
|
2055
2075
|
}
|
|
2056
|
-
.appbar__who:focus-visible { outline: var(--focus); outline-offset: 2px; }
|
|
2057
2076
|
.appbar__title, .appbar__sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.35; } /* clipped lines: room for Thai marks */
|
|
2058
2077
|
.appbar__title { font: var(--type-label); }
|
|
2059
2078
|
.appbar__sub { font: var(--type-caption); color: var(--on-surface-muted); }
|
|
@@ -2077,7 +2096,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2077
2096
|
.icon-btn--invert { background: var(--icon-btn-invert-bg, var(--accent)); color: var(--icon-btn-invert-ink, var(--on-accent)); border-color: transparent; }
|
|
2078
2097
|
.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); }
|
|
2079
2098
|
/* fab: the square floating action, bottom-right of a stage or screen */
|
|
2080
|
-
.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:
|
|
2099
|
+
.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); }
|
|
2081
2100
|
|
|
2082
2101
|
/* ---- buttons ---- */
|
|
2083
2102
|
.btn {
|
|
@@ -2110,7 +2129,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
2110
2129
|
/* disabled keeps the slot — a control that vanishes moves everything else,
|
|
2111
2130
|
and a pager whose "previous" disappeared on page one would shift the bar
|
|
2112
2131
|
under the thumb on every page */
|
|
2113
|
-
:is(.btn, .icon-btn):disabled { opacity: 0.4; cursor: default; }
|
|
2114
2132
|
/* a row of buttons sharing the width (twin CTAs inside a card) */
|
|
2115
2133
|
.btn-row { display: flex; gap: var(--sp-2); }
|
|
2116
2134
|
.btn-row > .btn { flex: 1; min-width: 0; }
|
|
@@ -2150,7 +2168,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2150
2168
|
/* ---- dropdown: the standard for every select — a pill trigger + .menu popover ----
|
|
2151
2169
|
<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>
|
|
2152
2170
|
<div class="menu" id="f1" popover><button class="menu__item" aria-current="true" data-value="week">this week</button>…</div></div>
|
|
2153
|
-
|
|
2171
|
+
The Dropdown component copies the picked item into the label and reports the change.
|
|
2154
2172
|
.menu (layover.css) draws the panel; anchor positioning pins it under the trigger. */
|
|
2155
2173
|
.dropdown { position: relative; min-width: 0; display: inline-grid; }
|
|
2156
2174
|
.dropdown__btn {
|
|
@@ -2169,12 +2187,11 @@ button { font: inherit; cursor: pointer; }
|
|
|
2169
2187
|
font: var(--type-label);
|
|
2170
2188
|
font-size: var(--fs-body);
|
|
2171
2189
|
text-align: left;
|
|
2172
|
-
cursor: pointer;
|
|
2173
2190
|
}
|
|
2174
2191
|
/* muted: the value is still the default (README 13) */
|
|
2175
2192
|
.dropdown--muted .dropdown__btn { color: var(--dropdown-muted-ink, var(--card-muted)); }
|
|
2176
2193
|
.dropdown__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2177
|
-
.dropdown__btn > .icon { transition: rotate
|
|
2194
|
+
.dropdown__btn > .icon { transition: rotate var(--motion-quick) ease; }
|
|
2178
2195
|
.dropdown:has(.menu:popover-open) .dropdown__btn > .icon { rotate: 180deg; }
|
|
2179
2196
|
.dropdown:has(.menu.\:popover-open) .dropdown__btn > .icon { rotate: 180deg; } /* the polyfill's open (layover.css) */
|
|
2180
2197
|
.dropdown--end .menu[popover] { position-area: block-end span-inline-start; } /* right-aligned trigger: panel grows leftwards (= .menu--end, layover.css) */
|
|
@@ -2184,10 +2201,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2184
2201
|
/* ---- segment (day picker / tab strip) — items share width, shrink on small screens ---- */
|
|
2185
2202
|
.segment { display: flex; gap: var(--sp-1); }
|
|
2186
2203
|
.segment__item {
|
|
2187
|
-
|
|
2188
|
-
background: transparent;
|
|
2189
|
-
color: inherit;
|
|
2190
|
-
opacity: 0.6;
|
|
2204
|
+
opacity: var(--rest);
|
|
2191
2205
|
border-radius: var(--r-chip);
|
|
2192
2206
|
padding: var(--sp-2) var(--sp-1);
|
|
2193
2207
|
font: var(--type-label);
|
|
@@ -2211,7 +2225,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2211
2225
|
:root { --bar-reserve: calc(var(--btn-h) + var(--sp-3) + var(--sp-5) + var(--sp-6)); }
|
|
2212
2226
|
.action-bar {
|
|
2213
2227
|
position: relative;
|
|
2214
|
-
z-index:
|
|
2228
|
+
z-index: var(--z-bar); /* above .sheet-stack sheets, which paint at --level 1-5 */
|
|
2215
2229
|
max-width: var(--screen-w);
|
|
2216
2230
|
margin-inline: auto;
|
|
2217
2231
|
padding: var(--sp-3) var(--sp-4) var(--sp-5);
|
|
@@ -2241,10 +2255,11 @@ button { font: inherit; cursor: pointer; }
|
|
|
2241
2255
|
NEVER carry words (Lh 2026-09-16, for good): an arrow is the whole meaning,
|
|
2242
2256
|
and a word that isn't a direction ("Get started", "Done") is not a pager
|
|
2243
2257
|
button — that step COMMITS and goes back to the plain .action-bar with one
|
|
2244
|
-
wide button. Where you are is said by the .pager__at dots
|
|
2245
|
-
that changes (a .screen__centre group, card.css)
|
|
2246
|
-
|
|
2247
|
-
|
|
2258
|
+
wide button. Where you are is said by the .pager__at dots — under the card
|
|
2259
|
+
that changes (a .screen__centre group, card.css) for a card walk, never in
|
|
2260
|
+
that bar (Lh, 2026-09-12); a walk of words instead floats its dots as the
|
|
2261
|
+
bar's own first row (step.css, Lh 2026-09-23) — either way the dots move
|
|
2262
|
+
with what moves. Between the buttons goes a "skip" link or nothing.
|
|
2248
2263
|
|
|
2249
2264
|
<div class="action-bar action-bar--pager">
|
|
2250
2265
|
<button class="icon-btn" aria-label="ย้อนกลับ" disabled>…</button>
|
|
@@ -2261,14 +2276,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
2261
2276
|
height: var(--dot);
|
|
2262
2277
|
border-radius: 50%;
|
|
2263
2278
|
background: currentColor;
|
|
2264
|
-
opacity:
|
|
2279
|
+
opacity: var(--faint);
|
|
2265
2280
|
}
|
|
2266
2281
|
.pager__at > .is-at { background: var(--accent); opacity: 1; }
|
|
2267
2282
|
/* ---- tiers: a control row above the buttons ----
|
|
2268
2283
|
A screen whose one move comes with a control — the introduction's text-size
|
|
2269
2284
|
slider above its pager. Each tier is a row; the bar stacks them, and any
|
|
2270
2285
|
parent hosting the bar publishes the room for both, the way the plain
|
|
2271
|
-
reserve is published above, so a .
|
|
2286
|
+
reserve is published above, so a .card--centre stops clear of it.
|
|
2272
2287
|
<div class="action-bar action-bar--tiers">
|
|
2273
2288
|
<div class="action-bar__tier"><label class="slider">…</label></div>
|
|
2274
2289
|
<div class="action-bar__tier action-bar__tier--pager">…the round pager…</div>
|
|
@@ -2315,6 +2330,57 @@ button { font: inherit; cursor: pointer; }
|
|
|
2315
2330
|
.action-note__words { display: block; } /* ONE run of words: an icon inside sits in the line, the spaces around it kept */
|
|
2316
2331
|
.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) */
|
|
2317
2332
|
|
|
2333
|
+
/* PRESS (Lh 2026-09-25; every tappable thing, 2026-09-27): every CONTROL answers a tap, INFORMATION never does (no pointer, no
|
|
2334
|
+
press). Two answers, by size: GIVE — a control the size of a finger shrinks under it (--press-scale) and springs back; DIM —
|
|
2335
|
+
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)
|
|
2336
|
+
instead, because a whole row shrinking reads as the card moving. A choice's fill fades in as it is picked. Disabled = no
|
|
2337
|
+
pointer, no press, --off. Zero specificity everywhere: a component's own rule always wins. A NEW control joins a list here. */
|
|
2338
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
2339
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
2340
|
+
a.avatar, button.avatar, .menu__item) {
|
|
2341
|
+
cursor: pointer;
|
|
2342
|
+
touch-action: manipulation;
|
|
2343
|
+
transition: scale var(--motion-press) ease-out;
|
|
2344
|
+
}
|
|
2345
|
+
:where(a.row, button.row, label.row, .row-expand > summary, .banner__summary, a.card, label.card, .toggle-field) {
|
|
2346
|
+
cursor: pointer;
|
|
2347
|
+
touch-action: manipulation;
|
|
2348
|
+
transition: opacity var(--motion-press) ease-out;
|
|
2349
|
+
}
|
|
2350
|
+
/* 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) */
|
|
2351
|
+
:where(.chip--pick, button.chip, .tile--pick, button.tile, .day, .segment__item, .calendar__day, .float-bar__item, .picker__item, .check,
|
|
2352
|
+
.icon-btn[role="tab"], .avatar-pick__item > .avatar) {
|
|
2353
|
+
transition-property: scale, background-color, color, border-color, opacity;
|
|
2354
|
+
transition-duration: var(--motion-press);
|
|
2355
|
+
transition-timing-function: ease-out;
|
|
2356
|
+
}
|
|
2357
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
2358
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
2359
|
+
a.avatar, button.avatar, .menu__item):active:not(:disabled, [aria-disabled="true"], :has(:disabled)) { scale: var(--press-scale); }
|
|
2360
|
+
: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); }
|
|
2361
|
+
.sheet-step:has(> .sheet-step__go:active) { opacity: var(--press-dim); } /* the step bar's button is invisible over it: the bar answers */
|
|
2362
|
+
/* SHADE — what has something BEHIND it (a peeked sheet over the pile, a postcard's face over its back) must not turn see-through:
|
|
2363
|
+
it darkens a touch instead (--press-shade) */
|
|
2364
|
+
:where(.sheet-stack--tap > .card:not(.is-open), .postcard__turn) { cursor: pointer; touch-action: manipulation; transition: filter var(--motion-press) ease-out; }
|
|
2365
|
+
:where(.sheet-stack--tap > .card:not(.is-open), .postcard__turn):active { filter: brightness(var(--press-shade)); }
|
|
2366
|
+
/* 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) */
|
|
2367
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
2368
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
2369
|
+
a.avatar, button.avatar, .menu__item):focus-visible,
|
|
2370
|
+
:where(.chip--pick, .tile--pick, label.btn, label.row, .toggle-field):has(:focus-visible) { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
2371
|
+
: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); }
|
|
2372
|
+
/* disabled: no pointer anywhere; a control fades (a link and a row keep their own muted look) */
|
|
2373
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
2374
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
2375
|
+
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"]),
|
|
2376
|
+
:where(.chip--pick, .tile--pick, .mood__opt, .toggle-field, label.btn, label.row):has(:disabled) { cursor: default; }
|
|
2377
|
+
:where(.btn, .icon-btn, .link, a.chip, button.chip, .chip--pick, a.tile, button.tile, .tile--pick, .day, .segment__item, .calendar__day,
|
|
2378
|
+
.float-bar__item, .avatar-pick__item, .add-row, .picker__item, .dropdown__btn, .appbar__who, .check, .toggle, .mood__opt, .sheet__handle,
|
|
2379
|
+
a.avatar, button.avatar, .menu__item):not(.link):is(:disabled, [aria-disabled="true"]),
|
|
2380
|
+
:where(.chip--pick, .tile--pick, .mood__opt, .toggle-field, label.btn):has(:disabled) { opacity: var(--off); }
|
|
2381
|
+
.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 */
|
|
2382
|
+
@media (prefers-reduced-motion: reduce) { :root { --press-scale: 1; } } /* the give goes; the dim, the shade and the focus ring stay */
|
|
2383
|
+
|
|
2318
2384
|
/* ---- css/numbers.css — ring, track, band, dot grid, picker, badge, bars ---- */
|
|
2319
2385
|
/* ============================================================
|
|
2320
2386
|
cardds/numbers.css — big numbers inside cards:
|
|
@@ -2351,7 +2417,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
2351
2417
|
}
|
|
2352
2418
|
.ring__value { font: var(--ring-value-font, var(--type-stat)); letter-spacing: var(--track-display); }
|
|
2353
2419
|
.ring__label { font: var(--ring-label-font, var(--type-caption)); color: var(--ring-label-ink, var(--card-muted)); }
|
|
2354
|
-
.card--3 .ring__label { color: inherit; opacity: 0.7; }
|
|
2355
2420
|
.ring--sm { --_size: var(--avatar-xl); --_w: var(--sp-3); }
|
|
2356
2421
|
.ring--sm .ring__value { font: 800 var(--fs-2xl)/1 var(--font-display); }
|
|
2357
2422
|
.ring--xs { --_size: var(--tap-sm); --_w: var(--dot-xs); } /* inline counter (step 3 of 5) */
|
|
@@ -2390,7 +2455,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2390
2455
|
content: "";
|
|
2391
2456
|
flex: 1;
|
|
2392
2457
|
height: var(--sp-3);
|
|
2393
|
-
background: color-mix(in srgb, var(--_ink) 30%, transparent)
|
|
2458
|
+
background: var(--ink-rail); /* the track's own ink at 30% — identical to color-mix(in srgb, var(--_ink) 30%, transparent): --ink-rail already reads currentColor, and .track sets color to --_ink */
|
|
2394
2459
|
}
|
|
2395
2460
|
.track__step:first-child::before { display: none; }
|
|
2396
2461
|
.track__step.is-done::before { background: var(--accent); }
|
|
@@ -2401,9 +2466,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
2401
2466
|
flex: none;
|
|
2402
2467
|
padding: calc((var(--tap-lg) - var(--icon)) / 2);
|
|
2403
2468
|
border-radius: 50%;
|
|
2404
|
-
background:
|
|
2469
|
+
background: var(--ink-rail);
|
|
2405
2470
|
color: var(--_ink);
|
|
2406
2471
|
}
|
|
2472
|
+
.track__step > .track__dot { display: grid; place-items: center; padding: 0; font: var(--track-dot-font, var(--type-title)); font-variant-numeric: tabular-nums; } /* a number in the round (TrackStep num) */
|
|
2407
2473
|
.track__step.is-done > .icon,
|
|
2408
2474
|
.track__step.is-done > .track__dot { background: var(--accent); color: var(--on-accent); }
|
|
2409
2475
|
|
|
@@ -2425,7 +2491,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
2425
2491
|
}
|
|
2426
2492
|
.band__num small { font-size: 0.5em; font-weight: 700; margin-right: 0.1em; } /* unit or % */
|
|
2427
2493
|
.band__foot { grid-area: foot; font: var(--band-foot-font, var(--type-caption)); color: var(--band-foot-ink, var(--card-muted)); }
|
|
2428
|
-
.card--3 .band__foot { color: inherit; opacity: 0.7; }
|
|
2429
2494
|
/* band stack: ONE card boundary; bands are frameless inside it, a hairline
|
|
2430
2495
|
between each. Bands may still carry slot tints (card--2 / card--3). */
|
|
2431
2496
|
.band-stack {
|
|
@@ -2472,14 +2537,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
2472
2537
|
@supports not (width: round(up, 1px, 1px)) {
|
|
2473
2538
|
.dotgrid::before { display: none; }
|
|
2474
2539
|
}
|
|
2475
|
-
.dotgrid--stars {
|
|
2476
|
-
background-image: none;
|
|
2477
|
-
}
|
|
2478
|
-
.dotgrid--stars,
|
|
2479
|
-
.dotgrid--stars::before {
|
|
2480
|
-
--_star: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
|
|
2481
|
-
}
|
|
2482
|
-
|
|
2483
2540
|
/* ---- picker: horizontal snap picker, centre value big ----
|
|
2484
2541
|
<div class="picker"><div class="picker__rail"><button class="picker__item">100</button>…</div><span class="picker__unit">ml</span></div> */
|
|
2485
2542
|
.picker { display: grid; gap: var(--picker-gap, var(--sp-1)); justify-items: center; position: relative; }
|
|
@@ -2494,20 +2551,15 @@ button { font: inherit; cursor: pointer; }
|
|
|
2494
2551
|
grid-auto-columns: 100%;
|
|
2495
2552
|
overflow-x: auto;
|
|
2496
2553
|
scroll-snap-type: x mandatory;
|
|
2497
|
-
scrollbar-width: none;
|
|
2498
2554
|
position: relative;
|
|
2499
2555
|
}
|
|
2500
|
-
.picker__rail::-webkit-scrollbar { display: none; }
|
|
2501
2556
|
.picker__item {
|
|
2502
2557
|
scroll-snap-align: center;
|
|
2503
|
-
border: 0;
|
|
2504
|
-
background: transparent;
|
|
2505
|
-
color: inherit;
|
|
2506
2558
|
padding: var(--picker-item-pad-y, var(--sp-2)) var(--picker-item-pad-x, 0);
|
|
2507
2559
|
font: var(--picker-item-font, var(--type-stat));
|
|
2508
2560
|
letter-spacing: var(--track-display);
|
|
2509
2561
|
font-variant-numeric: tabular-nums;
|
|
2510
|
-
opacity:
|
|
2562
|
+
opacity: var(--faint);
|
|
2511
2563
|
text-align: center;
|
|
2512
2564
|
}
|
|
2513
2565
|
.picker__item[aria-selected="true"] { opacity: 1; }
|
|
@@ -2524,12 +2576,11 @@ button { font: inherit; cursor: pointer; }
|
|
|
2524
2576
|
}
|
|
2525
2577
|
.picker::after { left: auto; right: calc(100% / 3); }
|
|
2526
2578
|
.picker__unit { font: var(--picker-unit-font, var(--type-caption)); color: var(--picker-unit-ink, var(--card-muted)); }
|
|
2527
|
-
.card--3 .picker__unit { color: inherit; opacity: 0.7; }
|
|
2528
2579
|
/* the scrolled-to item lights up by position — no JS */
|
|
2529
2580
|
@supports (animation-timeline: view(x)) {
|
|
2530
|
-
.picker__item {
|
|
2581
|
+
.picker__item { animation: picker-focus linear both; animation-timeline: view(x); } /* opacity: var(--faint) already set above — the animation alone is new here */
|
|
2531
2582
|
@keyframes picker-focus {
|
|
2532
|
-
0%, 100% { opacity:
|
|
2583
|
+
0%, 100% { opacity: var(--faint); }
|
|
2533
2584
|
45%, 55% { opacity: 1; }
|
|
2534
2585
|
}
|
|
2535
2586
|
}
|
|
@@ -2625,7 +2676,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2625
2676
|
.avatar--xl { --_size: var(--avatar-xl); } /* sheet head icon ring */
|
|
2626
2677
|
/* outline avatar: no fill, an edge — icons in a ring, "add" slots */
|
|
2627
2678
|
.avatar--outline { background: transparent; color: inherit; border: var(--border-w) solid var(--avatar-outline-border, var(--outline)); }
|
|
2628
|
-
.avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline));
|
|
2679
|
+
.avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline)); }
|
|
2629
2680
|
/* accent avatar: the "+7" tail, the selected person */
|
|
2630
2681
|
.avatar--on { background: var(--avatar-on-bg, var(--accent)); color: var(--avatar-on-ink, var(--on-accent)); }
|
|
2631
2682
|
/* halo: faint concentric rings behind a hero avatar (decorative) */
|
|
@@ -2651,10 +2702,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2651
2702
|
display: flex;
|
|
2652
2703
|
gap: var(--avatar-pick-gap, var(--sp-4));
|
|
2653
2704
|
overflow-x: auto;
|
|
2654
|
-
scroll-snap-type: x proximity;
|
|
2655
|
-
scrollbar-width: none; /* starts at the card's padding edge, like every row */
|
|
2705
|
+
scroll-snap-type: x proximity; /* starts at the card's padding edge, like every row */
|
|
2656
2706
|
}
|
|
2657
|
-
.avatar-pick::-webkit-scrollbar { display: none; }
|
|
2658
2707
|
.avatar-pick__item {
|
|
2659
2708
|
scroll-snap-align: start;
|
|
2660
2709
|
flex: none;
|
|
@@ -2662,17 +2711,13 @@ button { font: inherit; cursor: pointer; }
|
|
|
2662
2711
|
display: grid;
|
|
2663
2712
|
justify-items: center;
|
|
2664
2713
|
gap: var(--avatar-pick-item-gap, var(--sp-2));
|
|
2665
|
-
border: 0;
|
|
2666
|
-
background: transparent;
|
|
2667
|
-
color: inherit;
|
|
2668
|
-
padding: 0;
|
|
2669
2714
|
text-align: center;
|
|
2670
2715
|
}
|
|
2671
2716
|
.avatar-pick__name { font: var(--avatar-pick-name-font, var(--type-label)); font-size: var(--avatar-pick-name-font-size, var(--fs-sm)); }
|
|
2672
2717
|
.avatar-pick__role { font: var(--avatar-pick-role-font, var(--type-caption)); color: var(--avatar-pick-role-ink, var(--card-muted)); margin-top: calc(-1 * var(--sp-1)); }
|
|
2673
|
-
.
|
|
2718
|
+
.avatar-pick__item[aria-pressed="true"] > .avatar { background: var(--avatar-pick-on-bg, var(--accent)); color: var(--avatar-pick-on-ink, var(--on-accent)); border-color: transparent; } /* the picked person */
|
|
2674
2719
|
|
|
2675
|
-
/* ---- css/choice.css — check, toggle,
|
|
2720
|
+
/* ---- css/choice.css — check, toggle, chip grid, days, calendar, mood, pin, composer ---- */
|
|
2676
2721
|
/* ============================================================
|
|
2677
2722
|
cardds/choice.css — thumb-size choice controls inside cards:
|
|
2678
2723
|
check circle · toggle · toggle chip · chip grid · day strip ·
|
|
@@ -2696,7 +2741,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
2696
2741
|
background: var(--check-bg, var(--action-bg));
|
|
2697
2742
|
display: grid;
|
|
2698
2743
|
place-items: center;
|
|
2699
|
-
cursor: pointer;
|
|
2700
2744
|
}
|
|
2701
2745
|
.check::after {
|
|
2702
2746
|
content: "";
|
|
@@ -2709,15 +2753,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
2709
2753
|
}
|
|
2710
2754
|
.check:checked { background: var(--accent); border-color: transparent; }
|
|
2711
2755
|
.check:checked::after { opacity: 1; }
|
|
2712
|
-
.check:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
2713
2756
|
.check--sm { width: var(--check-sm-size, var(--tap-sm)); height: var(--check-sm-size, var(--tap-sm)); }
|
|
2714
2757
|
|
|
2715
2758
|
/* ---- toggle: big switch ----
|
|
2716
2759
|
<input type="checkbox" role="switch" class="toggle" aria-label="Private"> */
|
|
2717
2760
|
.toggle {
|
|
2718
2761
|
--_w: var(--bar-h); /* 64 × 36, knob = height − 2 × pad */
|
|
2719
|
-
--_h:
|
|
2720
|
-
--_pad:
|
|
2762
|
+
--_h: calc(var(--tap-base) * 3 / 4);
|
|
2763
|
+
--_pad: calc(var(--tap-base) / 16);
|
|
2721
2764
|
appearance: none;
|
|
2722
2765
|
width: var(--_w);
|
|
2723
2766
|
height: var(--_h);
|
|
@@ -2727,8 +2770,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2727
2770
|
border: var(--border-w) solid var(--toggle-border, var(--outline));
|
|
2728
2771
|
background: var(--toggle-bg, var(--action-bg));
|
|
2729
2772
|
position: relative;
|
|
2730
|
-
|
|
2731
|
-
transition: background 0.2s ease;
|
|
2773
|
+
transition: background-color var(--motion-press) ease-out, border-color var(--motion-press) ease-out;
|
|
2732
2774
|
}
|
|
2733
2775
|
.toggle::after {
|
|
2734
2776
|
content: "";
|
|
@@ -2740,11 +2782,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
2740
2782
|
border-radius: 50%;
|
|
2741
2783
|
background: currentColor;
|
|
2742
2784
|
opacity: 0.45;
|
|
2743
|
-
transition: translate
|
|
2785
|
+
transition: translate var(--motion-quick) ease, opacity var(--motion-quick) ease;
|
|
2744
2786
|
}
|
|
2745
2787
|
.toggle:checked { background: var(--accent); border-color: transparent; }
|
|
2746
2788
|
.toggle:checked::after { translate: calc(var(--_w) - var(--_h)) 0; background: var(--on-accent); opacity: 1; }
|
|
2747
|
-
.toggle:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
2748
2789
|
|
|
2749
2790
|
/* ---- chip pick: a chip that is a choice — a native radio/checkbox inside the label, the picked one filled with accent.
|
|
2750
2791
|
Full-width in a .chip-grid (one slot / many options), or hugging its word on a .chip-row with --sign (an interest
|
|
@@ -2760,11 +2801,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
2760
2801
|
chip grid the whole point is equal, full-width pick targets, so stretch.
|
|
2761
2802
|
Matters most in scripts without word spaces (Thai): min-content is the
|
|
2762
2803
|
whole string, tracks get wide, and hugging chips leave a ragged grid. */
|
|
2763
|
-
.chip-grid > .chip { justify-self: stretch; justify-content: center; min-height: var(--btn-h); padding-inline: var(--sp-2);
|
|
2764
|
-
.chip--pick {
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
.chip--pick
|
|
2804
|
+
.chip-grid > .chip { justify-self: stretch; justify-content: center; min-height: var(--btn-h); padding-inline: var(--sp-2); }
|
|
2805
|
+
.chip--pick { position: relative; } /* the input covers the whole chip, on a row as in a grid */
|
|
2806
|
+
/* the hidden input, the same shape wherever a native radio/checkbox drives a picked look:
|
|
2807
|
+
a chip, a mood dot, a tile (media.css) */
|
|
2808
|
+
:is(.chip--pick, .mood__opt, .tile--pick) > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: inherit; }
|
|
2809
|
+
:is(.chip--pick, .tile--pick):has(:checked) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
2768
2810
|
/* --sign: the trailing + that turns 45° into an × when picked — one mask, one rotate, no swap (Lh 2026-09-16) */
|
|
2769
2811
|
.chip--sign { padding-right: var(--sp-2); }
|
|
2770
2812
|
.chip--sign::after {
|
|
@@ -2797,11 +2839,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2797
2839
|
padding-block: var(--day-pad-y, var(--sp-3)); /* equal air top and bottom, so the selected pill
|
|
2798
2840
|
is the same shape on every day */
|
|
2799
2841
|
padding-inline: 0;
|
|
2800
|
-
border: 0;
|
|
2801
2842
|
border-radius: var(--day-radius, var(--r-chip));
|
|
2802
|
-
|
|
2803
|
-
color: inherit;
|
|
2804
|
-
opacity: 0.6;
|
|
2843
|
+
opacity: var(--rest);
|
|
2805
2844
|
}
|
|
2806
2845
|
.day__name { font: var(--day-name-font, var(--type-caption)); }
|
|
2807
2846
|
.day__num { font: 700 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
|
|
@@ -2814,17 +2853,13 @@ button { font: inherit; cursor: pointer; }
|
|
|
2814
2853
|
.calendar__head { display: flex; align-items: center; justify-content: space-between; gap: var(--calendar-head-gap, var(--sp-2)); }
|
|
2815
2854
|
.calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--calendar-grid-gap, var(--sp-1)); justify-items: center; }
|
|
2816
2855
|
.calendar__wd { font: var(--calendar-wd-font, var(--type-caption)); color: var(--calendar-wd-ink, var(--card-muted)); padding-block: var(--calendar-wd-pad-y, var(--sp-2)); }
|
|
2817
|
-
.card--3 .calendar__wd { color: inherit; opacity: 0.7; }
|
|
2818
2856
|
.calendar__day {
|
|
2819
2857
|
width: 100%;
|
|
2820
2858
|
max-width: var(--calendar-day-max-w, var(--tap));
|
|
2821
2859
|
aspect-ratio: 1;
|
|
2822
2860
|
display: grid;
|
|
2823
2861
|
place-items: center;
|
|
2824
|
-
border: 0;
|
|
2825
2862
|
border-radius: 50%;
|
|
2826
|
-
background: transparent;
|
|
2827
|
-
color: inherit;
|
|
2828
2863
|
font: 500 var(--fs-title)/1 var(--font-ui);
|
|
2829
2864
|
font-variant-numeric: tabular-nums;
|
|
2830
2865
|
grid-column-start: var(--start, auto); /* --start on the 1st: weekday column 1–7 */
|
|
@@ -2847,8 +2882,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2847
2882
|
display: flex;
|
|
2848
2883
|
justify-content: space-between;
|
|
2849
2884
|
}
|
|
2850
|
-
.mood__opt { flex: 1 1 0; max-width: var(--mood-opt-max-w, var(--tap)); min-width: 0; height: var(--mood-opt-h, var(--tap)); display: grid; place-items: center; position: relative;
|
|
2851
|
-
.mood__opt > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
2885
|
+
.mood__opt { flex: 1 1 0; max-width: var(--mood-opt-max-w, var(--tap)); min-width: 0; height: var(--mood-opt-h, var(--tap)); display: grid; place-items: center; position: relative; }
|
|
2852
2886
|
.mood__dot {
|
|
2853
2887
|
width: var(--mood-dot-size, var(--icon-xs));
|
|
2854
2888
|
height: var(--mood-dot-size, var(--icon-xs));
|
|
@@ -2879,12 +2913,11 @@ button { font: inherit; cursor: pointer; }
|
|
|
2879
2913
|
text-align: center;
|
|
2880
2914
|
font: var(--pin-cell-font, var(--type-stat));
|
|
2881
2915
|
font-variant-numeric: tabular-nums;
|
|
2916
|
+
caret-color: transparent; /* the cell the cursor waits in (Lh 2026-09-22): NO outline ring — it sat outside the pill and got cropped by the sheet's
|
|
2917
|
+
edge. The sign is INSIDE the pill: a full-ink border and a blinking bar drawn where the digit will land (the native caret
|
|
2918
|
+
is hidden — its height and place are the browser's; the pill's bar is one rule). A filled cell selects its digit instead. */
|
|
2882
2919
|
}
|
|
2883
2920
|
.pin__cell:not(:placeholder-shown) { color: var(--pin-cell-filled-ink, var(--accent)); } /* a typed digit: coloured words on the cell's own ground — no fill (Lh 2026-09-24; was an accent-filled pill) */
|
|
2884
|
-
/* the cell the cursor waits in (Lh 2026-09-22): NO outline ring — it sat outside the pill and got cropped by the sheet's
|
|
2885
|
-
edge. The sign is INSIDE the pill: a full-ink border and a blinking bar drawn where the digit will land (the native caret
|
|
2886
|
-
is hidden — its height and place are the browser's; the pill's bar is one rule). A filled cell selects its digit instead. */
|
|
2887
|
-
.pin__cell { caret-color: transparent; }
|
|
2888
2921
|
.pin--secret .pin__cell { -webkit-text-security: disc; text-security: disc; } /* a PIN: a dot per digit, the number pad kept (Lh 2026-09-25) */
|
|
2889
2922
|
.pin__cell:focus { outline: none; border-color: var(--pin-cell-active-border, currentColor); }
|
|
2890
2923
|
.pin__cell:focus:placeholder-shown {
|
|
@@ -2914,10 +2947,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
2914
2947
|
letter-spacing: var(--track-h2);
|
|
2915
2948
|
outline: none;
|
|
2916
2949
|
}
|
|
2917
|
-
.composer__input::placeholder { color: inherit; opacity:
|
|
2950
|
+
.composer__input::placeholder { color: inherit; opacity: var(--off); }
|
|
2918
2951
|
.composer__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--composer-bar-gap, var(--sp-3)); }
|
|
2919
2952
|
.composer__hint { font: var(--composer-hint-font, var(--type-caption)); color: var(--composer-hint-ink, var(--card-muted)); }
|
|
2920
|
-
.card--3 .composer__hint { color: inherit; opacity: 0.7; }
|
|
2921
2953
|
|
|
2922
2954
|
/* ---- slider: one value along a track, a thumb-size knob ----
|
|
2923
2955
|
<label class="slider"><span class="slider__label">ขนาดตัวหนังสือ</span><input class="slider__range" type="range" min="100" max="160" step="15" aria-label="ขนาดตัวหนังสือ"></label>
|
|
@@ -2928,8 +2960,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2928
2960
|
.slider { display: grid; gap: var(--slider-gap, var(--sp-1)); flex: 1; min-width: 0; position: relative; } /* relative: the glyph rides the knob */
|
|
2929
2961
|
/* ---- the sample glyph: one letter IN the knob, on-accent, at the type size
|
|
2930
2962
|
the slider sets — so a text-size slider shows the size before anything is
|
|
2931
|
-
read (Lh, 2026-09-12).
|
|
2932
|
-
as --slider-at (0..1) from the input's value; the glyph sits over the knob
|
|
2963
|
+
read (Lh, 2026-09-12). The Slider component publishes the knob's place on
|
|
2964
|
+
the .slider as --slider-at (0..1) from the input's value; the glyph sits over the knob
|
|
2933
2965
|
and never takes the pointer. <span class="slider__glyph" aria-hidden="true">ก</span>
|
|
2934
2966
|
before the input. */
|
|
2935
2967
|
.slider__glyph {
|
|
@@ -2979,6 +3011,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2979
3011
|
box-shadow: var(--shadow-float);
|
|
2980
3012
|
}
|
|
2981
3013
|
.slider__range:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
3014
|
+
.slider__range:active::-webkit-slider-thumb { scale: var(--press-scale); } /* the knob gives under the finger like any control */
|
|
3015
|
+
.slider__range:active::-moz-range-thumb { scale: var(--press-scale); }
|
|
2982
3016
|
|
|
2983
3017
|
/* ---- a switch that names itself (Toggle text, Lh 2026-09-23 — "อยู่บ้าน" on the home's top): the words, then the switch,
|
|
2984
3018
|
one label — the whole of it is the tap target ---- */
|
|
@@ -2986,8 +3020,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2986
3020
|
display: inline-flex; align-items: center; gap: var(--toggle-field-gap, var(--sp-2));
|
|
2987
3021
|
min-height: var(--tap);
|
|
2988
3022
|
font: var(--toggle-field-font, var(--type-label));
|
|
2989
|
-
|
|
2990
|
-
-webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
|
|
3023
|
+
-webkit-user-select: none; user-select: none;
|
|
2991
3024
|
flex: none;
|
|
2992
3025
|
}
|
|
2993
3026
|
|
|
@@ -3034,7 +3067,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3034
3067
|
who liked a postcard, the rest of a list. An end chevron turns when open. The app owns the list.
|
|
3035
3068
|
<details class="row-expand"><summary class="row">…lead · body · end…</summary><div class="row-expand__more">…</div></details> */
|
|
3036
3069
|
.row-expand { display: block; }
|
|
3037
|
-
.row-expand > summary { list-style: none;
|
|
3070
|
+
.row-expand > summary { list-style: none; }
|
|
3038
3071
|
.row-expand > summary::-webkit-details-marker { display: none; }
|
|
3039
3072
|
.row-expand > summary > .icon:last-child { transition: rotate var(--motion-slide); }
|
|
3040
3073
|
.row-expand[open] > summary > .icon:last-child { rotate: 180deg; }
|
|
@@ -3186,14 +3219,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
3186
3219
|
align-items: center;
|
|
3187
3220
|
gap: var(--link-gap, var(--sp-1));
|
|
3188
3221
|
min-height: var(--link-min-h, var(--tap-sm));
|
|
3189
|
-
color: inherit;
|
|
3190
3222
|
text-decoration: none;
|
|
3191
3223
|
font: var(--link-font, var(--type-label));
|
|
3192
3224
|
font-size: var(--link-font-size, var(--fs-body));
|
|
3193
|
-
border: 0;
|
|
3194
|
-
background: transparent;
|
|
3195
|
-
padding: 0;
|
|
3196
|
-
cursor: pointer;
|
|
3197
3225
|
}
|
|
3198
3226
|
/* a SMALL link (Lh 2026-09-23): a thing tapped rarely — "details", a ctaLink — at the minor-link size (15) and 28px of height;
|
|
3199
3227
|
its 44px tap target stays, an invisible ::after around it */
|
|
@@ -3220,7 +3248,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3220
3248
|
--card-ink: var(--card-3-ink);
|
|
3221
3249
|
--accent: var(--card-3-ink);
|
|
3222
3250
|
--on-accent: var(--card-3-bg);
|
|
3223
|
-
min-height:
|
|
3251
|
+
min-height: var(--_cover-min-h);
|
|
3224
3252
|
align-content: space-between;
|
|
3225
3253
|
overflow: hidden;
|
|
3226
3254
|
isolation: isolate;
|
|
@@ -3260,7 +3288,6 @@ button { font: inherit; cursor: pointer; }
|
|
|
3260
3288
|
.quote__text { margin: 0; font: var(--quote-text-font, var(--type-h1)); letter-spacing: var(--track-h1); text-wrap: balance; }
|
|
3261
3289
|
.quote--sm .quote__text { font: var(--type-h2); letter-spacing: var(--track-h2); }
|
|
3262
3290
|
.quote__by { font: var(--quote-by-font, var(--type-caption)); color: var(--quote-by-ink, var(--card-muted)); }
|
|
3263
|
-
.card--3 .quote__by { color: inherit; opacity: 0.7; }
|
|
3264
3291
|
|
|
3265
3292
|
/* ---- tile grid: big square tiles — icon top, label bottom ----
|
|
3266
3293
|
<div class="tile-grid"><button class="tile"><svg class="icon"/><span class="tile__label">Light</span></button>
|
|
@@ -3286,20 +3313,16 @@ button { font: inherit; cursor: pointer; }
|
|
|
3286
3313
|
font: inherit;
|
|
3287
3314
|
text-align: left;
|
|
3288
3315
|
position: relative;
|
|
3289
|
-
cursor: pointer;
|
|
3290
3316
|
}
|
|
3291
3317
|
.tile__label { font: var(--tile-label-font, var(--type-title)); }
|
|
3292
3318
|
.tile__value { font: var(--tile-value-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
|
|
3293
|
-
.tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity:
|
|
3319
|
+
.tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity: var(--dim); }
|
|
3294
3320
|
.tile__text { display: grid; gap: var(--tile-text-gap, var(--sp-0)); }
|
|
3295
3321
|
.tile--on { background: var(--tile-on-bg, var(--accent)); color: var(--tile-on-ink, var(--on-accent)); border-color: transparent; }
|
|
3296
|
-
.tile--pick
|
|
3297
|
-
.tile--pick:has(:checked) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
3298
|
-
.tile--pick:has(:focus-visible) { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
3322
|
+
/* .tile--pick's hidden input and :has(:checked) fill are declared once in choice.css, alongside .chip--pick and .mood__opt */
|
|
3299
3323
|
.tile--wide { grid-column: 1 / -1; }
|
|
3300
3324
|
/* stat: READ, not pressed — no frame of its own inside a card (a frame says "tap me"): icon · value · words on the card's ground */
|
|
3301
|
-
.tile--stat { border-color: transparent; background: none; padding-inline: 0;
|
|
3302
|
-
.tile--stat { min-height: var(--tile-stat-min-h, var(--avatar-xl)); gap: var(--tile-stat-gap, var(--sp-3)); grid-template-columns: auto 1fr; align-items: center; }
|
|
3325
|
+
.tile--stat { border-color: transparent; background: none; padding-inline: 0; min-height: var(--tile-stat-min-h, var(--avatar-xl)); gap: var(--tile-stat-gap, var(--sp-3)); grid-template-columns: auto 1fr; align-items: center; }
|
|
3303
3326
|
/* upload: dashed, centred plus + label */
|
|
3304
3327
|
.tile--upload {
|
|
3305
3328
|
border-style: dashed;
|
|
@@ -3322,7 +3345,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3322
3345
|
the corner read as a notch with a line through it rather than a lifted flap.
|
|
3323
3346
|
The COLOUR goes transparent rather than the border going to 0, so the box
|
|
3324
3347
|
keeps its geometry and a fold card still measures the same as the cards
|
|
3325
|
-
beside it
|
|
3348
|
+
beside it. */
|
|
3326
3349
|
.card--fold {
|
|
3327
3350
|
border-color: transparent;
|
|
3328
3351
|
border-bottom-right-radius: 0;
|
|
@@ -3376,8 +3399,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
3376
3399
|
overflow: hidden;
|
|
3377
3400
|
}
|
|
3378
3401
|
.mosaic > :first-child { grid-row: span 2; }
|
|
3379
|
-
.mosaic > * { min-height: 0; border-radius: 0;
|
|
3380
|
-
.mosaic > .ph { opacity:
|
|
3402
|
+
.mosaic > * { min-height: 0; border-radius: 0; } /* opacity/border-width here were no-ops: .ph restates its own below, and nothing else in a mosaic carries either by default */
|
|
3403
|
+
.mosaic > .ph { opacity: var(--off); border-width: var(--border-w); }
|
|
3381
3404
|
.mosaic > img { width: 100%; height: 100%; object-fit: cover; }
|
|
3382
3405
|
|
|
3383
3406
|
/* ---- postcard: a member's introduction in the shape of a cover card — the photo fills it,
|
|
@@ -3406,7 +3429,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3406
3429
|
liked it, a voice note, a reply live OUTSIDE the postcard, in a Row under it. */
|
|
3407
3430
|
.postcard {
|
|
3408
3431
|
position: relative;
|
|
3409
|
-
min-height: var(--postcard-min-h,
|
|
3432
|
+
min-height: var(--postcard-min-h, var(--_cover-min-h));
|
|
3410
3433
|
perspective: 60rem;
|
|
3411
3434
|
border-radius: var(--postcard-radius, var(--r-card));
|
|
3412
3435
|
isolation: isolate;
|
|
@@ -3432,14 +3455,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
3432
3455
|
display: grid;
|
|
3433
3456
|
align-content: start;
|
|
3434
3457
|
padding: var(--postcard-face-pad, var(--card-pad-rest));
|
|
3435
|
-
border: 0;
|
|
3436
3458
|
border-radius: inherit;
|
|
3437
|
-
background: transparent;
|
|
3438
|
-
color: inherit;
|
|
3439
3459
|
text-align: left;
|
|
3440
|
-
cursor: pointer;
|
|
3441
3460
|
}
|
|
3442
|
-
.postcard__turn:focus-visible { outline
|
|
3461
|
+
.postcard__turn:focus-visible { outline-offset: calc(-1 * var(--sp-1)); } /* the ring inside the card's edge */
|
|
3443
3462
|
/* the front: the photo (or the wireframe box) fills it; the band reads on a scrim */
|
|
3444
3463
|
.postcard__front { background: var(--postcard-front-bg, var(--card-3-bg)); color: var(--postcard-front-ink, var(--card-3-ink)); }
|
|
3445
3464
|
.postcard__front .postcard__turn > img,
|
|
@@ -3495,11 +3514,11 @@ button { font: inherit; cursor: pointer; }
|
|
|
3495
3514
|
.postcard__face { transition: none; }
|
|
3496
3515
|
}
|
|
3497
3516
|
|
|
3498
|
-
/* ---- css/layover.css — ask drawer, popover menu, banner, float bar, callout
|
|
3517
|
+
/* ---- css/layover.css — ask drawer, popover menu, banner, float bar, callout ---- */
|
|
3499
3518
|
/* ============================================================
|
|
3500
3519
|
cardds/layover.css — cards that lay OVER other content:
|
|
3501
3520
|
Modal (ask) · popover menu · expanding banner · float bar ·
|
|
3502
|
-
callout
|
|
3521
|
+
callout. The sheet (peek / half / full / page)
|
|
3503
3522
|
lives in sheet.css; these compose with .sheet-stage.
|
|
3504
3523
|
============================================================ */
|
|
3505
3524
|
|
|
@@ -3522,10 +3541,11 @@ button { font: inherit; cursor: pointer; }
|
|
|
3522
3541
|
2. the drawer comes out of the card's bottom edge while the lift moves
|
|
3523
3542
|
up, both at once: the card grows from its centre, which never moves
|
|
3524
3543
|
(the modal centres itself — absolute, margin auto; a growing card keeps its middle where it is)
|
|
3525
|
-
Closing:
|
|
3526
|
-
reverse (
|
|
3527
|
-
|
|
3528
|
-
|
|
3544
|
+
Closing: <Modal open onClosed> — set open={false} and the Modal component plays the
|
|
3545
|
+
same animations in reverse (Animation.reverse() on each — nothing duplicated), then
|
|
3546
|
+
calls onClosed for the app to unmount the stage. Modal no longer knows its own
|
|
3547
|
+
buttons (data-answer / the "cardds:modal" event are gone; the app's own onClick
|
|
3548
|
+
sets open={false}).
|
|
3529
3549
|
Reduced motion: the finished state, no beats; closing fires at once. */
|
|
3530
3550
|
.modal {
|
|
3531
3551
|
--drawer-h: 14rem;
|
|
@@ -3570,7 +3590,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3570
3590
|
--accent: var(--card-3-ink); /* the one solid button inverts on the dark drawer */
|
|
3571
3591
|
--on-accent: var(--card-3-bg);
|
|
3572
3592
|
}
|
|
3573
|
-
.modal__drawer .t-caption { opacity:
|
|
3593
|
+
.modal__drawer .t-caption { opacity: var(--dim); }
|
|
3574
3594
|
.modal__drawer .icon-btn { background: transparent; color: inherit; border-color: var(--ink-rail); }
|
|
3575
3595
|
.modal__drawer .icon-btn--invert { background: var(--accent); color: var(--on-accent); border-color: transparent; width: var(--tap-lg); height: var(--tap-lg); }
|
|
3576
3596
|
@media (prefers-reduced-motion: no-preference) {
|
|
@@ -3604,8 +3624,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
3604
3624
|
element the button belongs to (a card, a sheet), opened by an .icon-btn:
|
|
3605
3625
|
<button class="icon-btn icon-btn--sm" popovertarget="m1" aria-label="More">…</button>
|
|
3606
3626
|
<div class="menu menu--actions menu--end" id="m1" popover><button class="menu__item"><svg class="icon"/>Edit</button>…</div>
|
|
3607
|
-
No item stays current there
|
|
3608
|
-
trigger sits at the far right, so the panel grows leftwards. */
|
|
3627
|
+
No item stays current there — an actions menu's items carry no aria-current, unlike
|
|
3628
|
+
the dropdown's. .menu--end = the trigger sits at the far right, so the panel grows leftwards. */
|
|
3609
3629
|
.menu {
|
|
3610
3630
|
display: none; /* explicit closed state — never display on the base rule */
|
|
3611
3631
|
margin: 0;
|
|
@@ -3631,18 +3651,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
3631
3651
|
min-height: var(--menu-item-min-h, var(--tap));
|
|
3632
3652
|
width: 100%;
|
|
3633
3653
|
padding: var(--menu-item-pad-y, 0) var(--menu-item-pad-x, var(--sp-3));
|
|
3634
|
-
border: 0;
|
|
3635
3654
|
/* concentric with the panel: the card radius minus the panel's padding, so the
|
|
3636
3655
|
highlight's curve follows the outer corner (Lh 2026-09-16) */
|
|
3637
3656
|
border-radius: calc(var(--r-card) - var(--menu-pad, var(--sp-2)));
|
|
3638
|
-
background: transparent;
|
|
3639
|
-
color: inherit;
|
|
3640
3657
|
font: var(--menu-item-font, var(--type-title));
|
|
3641
3658
|
text-align: left;
|
|
3642
|
-
cursor: pointer;
|
|
3643
3659
|
}
|
|
3644
3660
|
.menu__item:hover, .menu__item:focus-visible { background: var(--ink-tint); outline: none; }
|
|
3645
|
-
.menu__item[aria-current]
|
|
3661
|
+
.menu__item[aria-current] { background: var(--accent); color: var(--on-accent); }
|
|
3646
3662
|
@supports (anchor-name: --a) {
|
|
3647
3663
|
.menu[popover] {
|
|
3648
3664
|
position-area: block-end span-inline-end;
|
|
@@ -3656,7 +3672,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3656
3672
|
.menu[popover] {
|
|
3657
3673
|
opacity: 0;
|
|
3658
3674
|
translate: 0 calc(-1 * var(--sp-2));
|
|
3659
|
-
transition: display
|
|
3675
|
+
transition: display var(--motion-quick) allow-discrete, overlay var(--motion-quick) allow-discrete, opacity var(--motion-quick), translate var(--motion-quick);
|
|
3660
3676
|
}
|
|
3661
3677
|
.menu:popover-open { opacity: 1; translate: 0 0; }
|
|
3662
3678
|
.menu.\:popover-open { opacity: 1; translate: 0 0; }
|
|
@@ -3677,19 +3693,19 @@ button { font: inherit; cursor: pointer; }
|
|
|
3677
3693
|
padding-inline: var(--card-pad, var(--screen-pad));
|
|
3678
3694
|
}
|
|
3679
3695
|
.banner__summary {
|
|
3680
|
-
list-style: none;
|
|
3696
|
+
list-style: none;
|
|
3681
3697
|
display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
|
|
3682
3698
|
padding-block: var(--banner-summary-pad-y, calc(var(--sp-base) * 1.5)); font: var(--banner-summary-font, var(--type-caption)); font-weight: 400;
|
|
3683
3699
|
}
|
|
3684
3700
|
.banner__summary::-webkit-details-marker { display: none; }
|
|
3685
|
-
.banner__summary .icon { transition: rotate
|
|
3701
|
+
.banner__summary .icon { transition: rotate var(--motion-quick) ease; }
|
|
3686
3702
|
.banner[open] .banner__summary .icon { rotate: 180deg; }
|
|
3687
3703
|
.banner__body { display: grid; gap: var(--banner-body-gap, var(--sp-1)); padding-bottom: var(--sp-4); }
|
|
3688
3704
|
.banner::details-content { width: 100%; }
|
|
3689
3705
|
@supports (interpolate-size: allow-keywords) {
|
|
3690
3706
|
@media (prefers-reduced-motion: no-preference) {
|
|
3691
3707
|
.banner { interpolate-size: allow-keywords; }
|
|
3692
|
-
.banner::details-content { block-size: 0; overflow: clip; transition: block-size
|
|
3708
|
+
.banner::details-content { block-size: 0; overflow: clip; transition: block-size var(--motion-quick) ease, content-visibility var(--motion-quick) allow-discrete; }
|
|
3693
3709
|
.banner[open]::details-content { block-size: auto; }
|
|
3694
3710
|
}
|
|
3695
3711
|
}
|
|
@@ -3706,16 +3722,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
3706
3722
|
display: flex;
|
|
3707
3723
|
gap: var(--float-bar-gap, var(--sp-2));
|
|
3708
3724
|
align-items: center;
|
|
3709
|
-
z-index:
|
|
3725
|
+
z-index: var(--z-bar);
|
|
3710
3726
|
}
|
|
3711
3727
|
/* in a fill screen (its `bottom`) the float bar floats over the SCREEN's foot, not the window's — it lies over the content
|
|
3712
3728
|
(a map, a stack), so it is not a row of the column; the screen is its box (Lh 2026-09-23) */
|
|
3713
3729
|
.screen--fill > .float-bar { position: absolute; }
|
|
3714
|
-
.sheet-stage > .float-bar { position: absolute; padding-inline:
|
|
3715
|
-
/* .screen's default bottom padding clears an .action-bar; a .float-bar is
|
|
3716
|
-
taller (--bar-h + its own bottom offset), so reserve the difference. Any
|
|
3717
|
-
parent that hosts the bar counts (see .action-bar's reserve, actions.css) */
|
|
3718
|
-
:has(> .float-bar) .screen:not(.screen--fill) { padding-bottom: calc(var(--bar-h) + var(--sp-5) + var(--sp-6)); }
|
|
3730
|
+
.sheet-stage > .float-bar { position: absolute; } /* padding-inline removed 2026-09-27: it hard-coded --sp-4 over the --float-bar-pad-x knob, so the knob had no effect here */
|
|
3719
3731
|
.float-bar__pill {
|
|
3720
3732
|
flex: 1;
|
|
3721
3733
|
min-width: 0;
|
|
@@ -3740,21 +3752,17 @@ button { font: inherit; cursor: pointer; }
|
|
|
3740
3752
|
gap: var(--float-bar-item-gap, var(--sp-0));
|
|
3741
3753
|
min-height: var(--float-bar-item-min-h, var(--tap-lg));
|
|
3742
3754
|
padding: var(--float-bar-item-pad, var(--sp-1));
|
|
3743
|
-
border: 0;
|
|
3744
3755
|
border-radius: var(--float-bar-item-radius, var(--r-chip));
|
|
3745
|
-
background: transparent;
|
|
3746
|
-
color: inherit;
|
|
3747
3756
|
font: var(--float-bar-item-font, var(--type-caption));
|
|
3748
3757
|
text-decoration: none;
|
|
3749
|
-
opacity:
|
|
3758
|
+
opacity: var(--rest);
|
|
3750
3759
|
overflow: hidden;
|
|
3751
3760
|
white-space: nowrap;
|
|
3752
3761
|
text-overflow: ellipsis;
|
|
3753
3762
|
}
|
|
3754
3763
|
/* aria-current="false" is the correct way to mark the OTHER tabs, so the
|
|
3755
3764
|
bare attribute selector lights every one of them up */
|
|
3756
|
-
.float-bar__item[aria-current]:not([aria-current="false"])
|
|
3757
|
-
.float-bar__item.is-on { background: var(--accent); color: var(--on-accent); opacity: 1; }
|
|
3765
|
+
.float-bar__item[aria-current]:not([aria-current="false"]) { background: var(--accent); color: var(--on-accent); opacity: 1; }
|
|
3758
3766
|
.float-bar > .icon-btn { width: var(--bar-h); height: var(--bar-h); box-shadow: var(--shadow-float); }
|
|
3759
3767
|
|
|
3760
3768
|
/* ---- callout: a small card with a tail — over a map, a media, a mascot ----
|
|
@@ -3788,18 +3796,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
3788
3796
|
border-left: 0;
|
|
3789
3797
|
}
|
|
3790
3798
|
.callout__title { font: var(--callout-title-font, var(--type-title)); }
|
|
3791
|
-
.callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity:
|
|
3792
|
-
/* ---- deck: swipe cards; the last child is on top ----
|
|
3793
|
-
<div class="deck"><article class="card">…</article><article class="card">…</article><article class="card card--fold">front</article></div>
|
|
3794
|
-
<div class="deck__actions"><button class="icon-btn">×</button><button class="icon-btn icon-btn--invert">✓</button></div> */
|
|
3795
|
-
.deck { display: grid; place-items: center; padding: var(--deck-pad-top, var(--sp-4)) var(--deck-pad-x, var(--sp-4)) var(--deck-pad-bottom, var(--sp-6)); }
|
|
3796
|
-
.deck > .card { grid-area: 1 / 1; width: min(100%, 20rem); min-height: 12.5rem; transition: rotate 0.3s ease, translate 0.3s ease; }
|
|
3797
|
-
.deck > .card:nth-last-child(2) { rotate: -4deg; translate: calc(-1 * var(--sp-2)) var(--sp-2); opacity: 0.85; }
|
|
3798
|
-
.deck > .card:nth-last-child(3) { rotate: 5deg; translate: var(--sp-3) var(--sp-4); opacity: 0.6; }
|
|
3799
|
-
.deck > .card:nth-last-child(n + 4) { display: none; }
|
|
3800
|
-
.deck__actions { display: flex; justify-content: center; gap: var(--deck-actions-gap, var(--sp-8)); }
|
|
3801
|
-
.deck__actions > .icon-btn { width: var(--bar-h); height: var(--bar-h); }
|
|
3802
|
-
|
|
3799
|
+
.callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity: var(--dim); }
|
|
3803
3800
|
/* ---- splash: the app's mark at the human centre while it boots (Lh 2026-09-24) ----
|
|
3804
3801
|
<section class="screen screen--fill splash"><div class="splash__group"><div class="splash__mark">{the app's lottie/img}</div>
|
|
3805
3802
|
<p class="splash__text">…</p></div></section>
|