@usableapp/cardds 0.3.0 → 0.4.1
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/README.md +12 -8
- package/css/actions.css +38 -27
- package/css/base.css +9 -9
- package/css/card.css +24 -23
- package/css/choice.css +50 -50
- package/css/forms.css +18 -18
- package/css/journey.css +16 -16
- package/css/layover.css +57 -60
- package/css/lists.css +52 -43
- package/css/media.css +96 -60
- package/css/numbers.css +41 -25
- package/css/people.css +11 -11
- package/css/sheet.css +28 -24
- package/css/stack.css +3 -3
- package/css/theme.template.css +6 -3
- package/dist/actions/Btn.d.ts +109 -108
- package/dist/actions/BtnRow.d.ts +2 -2
- package/dist/actions/Chip.d.ts +102 -101
- package/dist/actions/Dot.d.ts +2 -2
- package/dist/actions/Dropdown.d.ts +4 -4
- package/dist/actions/Fab.d.ts +2 -2
- package/dist/actions/IconBtn.d.ts +107 -106
- package/dist/actions/Link.d.ts +105 -104
- package/dist/actions/Segment.d.ts +3 -3
- package/dist/cardds.css +443 -369
- package/dist/cards/Card.d.ts +99 -98
- package/dist/cards/CardFoot.d.ts +2 -2
- package/dist/cards/CardHead.d.ts +2 -2
- package/dist/cards/CardParts.d.ts +7 -7
- package/dist/cards/Placeholder.d.ts +2 -2
- package/dist/cards/Stat.d.ts +7 -5
- package/dist/cards/Stat.js +2 -2
- package/dist/choice/Calendar.d.ts +2 -2
- package/dist/choice/Check.d.ts +3 -3
- package/dist/choice/DayStrip.d.ts +3 -3
- package/dist/choice/Mood.d.ts +2 -2
- package/dist/choice/Slider.d.ts +2 -2
- package/dist/forms/AddRow.d.ts +2 -2
- package/dist/forms/Composer.d.ts +2 -2
- package/dist/forms/Field.d.ts +2 -2
- package/dist/forms/FileBtn.d.ts +2 -2
- package/dist/forms/Pin.d.ts +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/journey/NoteRow.d.ts +2 -2
- package/dist/journey/Route.d.ts +2 -2
- package/dist/journey/TileBadge.d.ts +2 -2
- package/dist/layover/Banner.d.ts +2 -2
- package/dist/layover/Callout.d.ts +2 -2
- package/dist/layover/Deck.d.ts +3 -3
- package/dist/lists/Bubble.d.ts +3 -3
- package/dist/lists/Kv.d.ts +5 -5
- package/dist/lists/Legend.d.ts +3 -3
- package/dist/lists/Row.d.ts +14 -285
- package/dist/lists/Row.js +10 -3
- package/dist/lists/Timeline.d.ts +3 -3
- package/dist/media/MapArea.d.ts +2 -2
- package/dist/media/Mosaic.d.ts +3 -3
- package/dist/media/Postcard.d.ts +17 -7
- package/dist/media/Postcard.js +8 -5
- package/dist/media/Quote.d.ts +2 -2
- package/dist/media/Tile.d.ts +5 -4
- package/dist/numbers/Badge.d.ts +2 -2
- package/dist/numbers/Band.d.ts +3 -3
- package/dist/numbers/Bars.d.ts +3 -3
- package/dist/numbers/DotGrid.d.ts +2 -2
- package/dist/numbers/Picker.d.ts +3 -3
- package/dist/numbers/Progress.d.ts +9 -0
- package/dist/numbers/Progress.js +7 -0
- package/dist/numbers/Ring.d.ts +2 -2
- package/dist/numbers/Track.d.ts +3 -3
- package/dist/people/Avatar.d.ts +100 -99
- package/dist/people/AvatarPick.d.ts +3 -3
- package/dist/scaffold/ActionBar.d.ts +3 -3
- package/dist/scaffold/AppBar.d.ts +2 -2
- package/dist/scaffold/BaseContent.d.ts +3 -3
- package/dist/scaffold/Centre.d.ts +3 -3
- package/dist/scaffold/EmptyState.d.ts +2 -2
- package/dist/scaffold/FilterRow.d.ts +2 -2
- package/dist/scaffold/FloatBar.d.ts +105 -104
- package/dist/scaffold/PagerAt.d.ts +2 -2
- package/dist/scaffold/Screen.d.ts +100 -99
- package/dist/scaffold/TopBar.d.ts +2 -2
- package/dist/sheets/Drawer.d.ts +7 -7
- package/dist/sheets/Drawer.js +1 -1
- package/dist/sheets/Sheet.d.ts +3 -5
- package/dist/sheets/Sheet.js +2 -2
- package/dist/sheets/SheetBody.d.ts +2 -2
- package/dist/sheets/SheetHead.d.ts +2 -2
- package/dist/sheets/SheetStack.d.ts +5 -5
- package/dist/sheets/SheetStack.js +3 -3
- package/dist/sheets/SheetStage.d.ts +5 -7
- package/dist/sheets/SheetStage.js +3 -3
- package/dist/sheets/StackBack.d.ts +7 -0
- package/dist/sheets/StackBack.js +7 -0
- package/dist/sheets/Step.d.ts +2 -2
- package/dist/type/Icon.d.ts +22 -7
- package/dist/type/Icon.js +27 -4
- package/dist/type/Text.d.ts +7 -1
- package/dist/type/Text.js +2 -2
- package/package.json +3 -2
- package/dist/sheets/CardBack.d.ts +0 -7
- package/dist/sheets/CardBack.js +0 -7
package/dist/cardds.css
CHANGED
|
@@ -417,10 +417,10 @@ img, svg { display: block; max-width: 100%; }
|
|
|
417
417
|
button { font: inherit; cursor: pointer; }
|
|
418
418
|
|
|
419
419
|
/* icon from icons.svg sprite: <svg class="icon"><use href="icons.svg#bell"/></svg> */
|
|
420
|
-
.icon { width: var(--icon); height: var(--icon); flex: none; }
|
|
421
|
-
.icon--sm { width: var(--icon-sm); height: var(--icon-sm); }
|
|
422
|
-
.icon--xs { width: var(--icon-xs); height: var(--icon-xs); } /* inside captions, chips, links */
|
|
423
|
-
.icon--lg { width: var(--icon-lg); height: var(--icon-lg); } /* empty states */
|
|
420
|
+
.icon { width: var(--icon-size, var(--icon)); height: var(--icon-size, var(--icon)); flex: none; }
|
|
421
|
+
.icon--sm { width: var(--icon-sm-size, var(--icon-sm)); height: var(--icon-sm-size, var(--icon-sm)); }
|
|
422
|
+
.icon--xs { width: var(--icon-xs-size, var(--icon-xs)); height: var(--icon-xs-size, var(--icon-xs)); } /* inside captions, chips, links */
|
|
423
|
+
.icon--lg { width: var(--icon-lg-size, var(--icon-lg)); height: var(--icon-lg-size, var(--icon-lg)); } /* empty states */
|
|
424
424
|
|
|
425
425
|
/* mobile screen column — the standard horizontal inset, and nothing vertical.
|
|
426
426
|
Top and bottom are zero on purpose: vertical room is only ever a RESERVE for
|
|
@@ -487,9 +487,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
487
487
|
In a .step it is the reading block and step.css adds the flex/scroll rule ---- */
|
|
488
488
|
.base-content {
|
|
489
489
|
display: grid;
|
|
490
|
-
gap: var(--sp-3);
|
|
490
|
+
gap: var(--base-content-gap, var(--sp-3));
|
|
491
491
|
align-content: start; /* a tall column never stretches the rows — the words hug the top */
|
|
492
|
-
padding-block: var(--sp-4);
|
|
492
|
+
padding-block: var(--base-content-pad-y, var(--sp-4));
|
|
493
493
|
}
|
|
494
494
|
.base-content > .t-body { color: var(--on-surface-muted); max-width: 34ch; }
|
|
495
495
|
.base-content .cta-pack {
|
|
@@ -505,9 +505,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
505
505
|
display: grid;
|
|
506
506
|
justify-items: center;
|
|
507
507
|
text-align: center;
|
|
508
|
-
gap: var(--sp-2);
|
|
509
|
-
padding: var(--sp-12) var(--sp-6);
|
|
510
|
-
color: 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 */
|
|
508
|
+
gap: var(--empty-state-gap, var(--sp-2));
|
|
509
|
+
padding: var(--empty-state-pad-y, var(--sp-12)) var(--empty-state-pad-x, var(--sp-6));
|
|
510
|
+
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 */
|
|
511
511
|
}
|
|
512
512
|
.empty-state__action { margin-top: var(--sp-2); }
|
|
513
513
|
/* inside a card (a section's "nothing yet"): the card's ink */
|
|
@@ -528,7 +528,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
528
528
|
background: var(--card-bg);
|
|
529
529
|
color: var(--card-ink);
|
|
530
530
|
border: var(--border-w) solid var(--card-border);
|
|
531
|
-
border-radius: var(--r-card);
|
|
531
|
+
border-radius: var(--card-radius, var(--r-card));
|
|
532
532
|
padding: var(--card-pad);
|
|
533
533
|
display: grid;
|
|
534
534
|
gap: var(--card-gap);
|
|
@@ -559,8 +559,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
559
559
|
flex-wrap: wrap; /* the caption / overline take a line of their own — no wrapper div */
|
|
560
560
|
align-items: center;
|
|
561
561
|
justify-content: space-between;
|
|
562
|
-
column-gap: var(--sp-3);
|
|
563
|
-
row-gap: var(--sp-0);
|
|
562
|
+
column-gap: var(--card-head-col-gap, var(--sp-3));
|
|
563
|
+
row-gap: var(--card-head-row-gap, var(--sp-0));
|
|
564
564
|
}
|
|
565
565
|
/* flat head markup (Lh 2026-09-16 — no <p>, no wrapper, only padding and gap tokens):
|
|
566
566
|
<div class="card__head"><span class="t-overline t-muted">…</span><h2 class="t-h2">…</h2><span class="t-caption t-muted">…</span><button class="icon-btn icon-btn--sm">…</button></div>
|
|
@@ -571,8 +571,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
571
571
|
.card__head > .t-overline { order: -1; }
|
|
572
572
|
.card__head--top { align-items: flex-start; }
|
|
573
573
|
|
|
574
|
-
.card__media { border-radius: var(--r-media); overflow: hidden; position: relative; } /* relative: corner badges */
|
|
575
|
-
.card__end { text-align: right; display: grid; gap: var(--sp-0); justify-items: end; } /* trailing value + caption */
|
|
574
|
+
.card__media { border-radius: var(--card-media-radius, var(--r-media)); overflow: hidden; position: relative; } /* relative: corner badges */
|
|
575
|
+
.card__end { text-align: right; display: grid; gap: var(--card-end-gap, var(--sp-0)); justify-items: end; } /* trailing value + caption */
|
|
576
576
|
.card__media > img { width: 100%; height: auto; object-fit: cover; }
|
|
577
577
|
|
|
578
578
|
/* ---- the card skeleton: head · content · foot ----
|
|
@@ -636,7 +636,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
636
636
|
/* foot: the card's action buttons on one line. One button takes the width,
|
|
637
637
|
up to four share it in even parts — so the gaps read even too — and from
|
|
638
638
|
three on they drop their side padding so the labels keep the room. */
|
|
639
|
-
.card__foot { display: flex; gap: var(--sp-2); align-items: center; }
|
|
639
|
+
.card__foot { display: flex; gap: var(--card-foot-gap, var(--sp-2)); align-items: center; }
|
|
640
640
|
.card__foot > .btn { flex: 1 1 0; min-width: 0; }
|
|
641
641
|
.card__foot:has(> :nth-child(3)) > .btn { padding-inline: var(--sp-2); }
|
|
642
642
|
|
|
@@ -770,51 +770,52 @@ button { font: inherit; cursor: pointer; }
|
|
|
770
770
|
.screen__centre > .pager__at { justify-self: center; }
|
|
771
771
|
|
|
772
772
|
/* compact card inside a card (nested note, promo) */
|
|
773
|
-
.card--sm { padding: var(--sp-4); --card-gap: var(--sp-2); border-radius: var(--r-card); }
|
|
773
|
+
.card--sm { padding: var(--card-sm-pad, var(--sp-4)); --card-gap: var(--sp-2); border-radius: var(--card-sm-radius, var(--r-card)); }
|
|
774
774
|
|
|
775
775
|
/* the sheet stack (.sheet-stack) — sheets stacked, heads peeking — lives in
|
|
776
776
|
stack.css; a stacked sheet is a plain .card, the material */
|
|
777
777
|
|
|
778
778
|
/* trailing icon cluster in a card header */
|
|
779
|
-
.icon-row { display: flex; align-items: center; gap: var(--sp-4); opacity: 0.85; }
|
|
779
|
+
.icon-row { display: flex; align-items: center; gap: var(--icon-row-gap, var(--sp-4)); opacity: 0.85; }
|
|
780
780
|
|
|
781
781
|
/* ---- card list: rows, each row its own boundary ---- */
|
|
782
|
-
.card-list { display: grid; gap: var(--sp-3); }
|
|
782
|
+
.card-list { display: grid; gap: var(--card-list-gap, var(--sp-3)); }
|
|
783
783
|
|
|
784
784
|
.card--row {
|
|
785
|
-
border-radius: var(--r-card);
|
|
786
|
-
padding: var(--sp-4);
|
|
785
|
+
border-radius: var(--card-row-radius, var(--r-card));
|
|
786
|
+
padding: var(--card-row-pad, var(--sp-4));
|
|
787
787
|
display: flex;
|
|
788
788
|
align-items: center;
|
|
789
|
-
gap: var(--sp-3);
|
|
789
|
+
gap: var(--card-row-gap, var(--sp-3));
|
|
790
790
|
}
|
|
791
791
|
.card--row .card__body { flex: 1; display: grid; gap: var(--sp-0); }
|
|
792
792
|
|
|
793
793
|
/* leading thumb/icon in a row */
|
|
794
794
|
.card__thumb {
|
|
795
|
-
width: var(--tap-sm); height: var(--tap-sm);
|
|
795
|
+
width: var(--card-thumb-size, var(--tap-sm)); height: var(--card-thumb-size, var(--tap-sm));
|
|
796
796
|
flex: none;
|
|
797
|
-
border-radius: var(--r-control);
|
|
797
|
+
border-radius: var(--card-thumb-radius, var(--r-control));
|
|
798
798
|
border: var(--border-w) solid currentColor;
|
|
799
799
|
display: grid;
|
|
800
800
|
place-items: center;
|
|
801
|
-
font: var(--type-label);
|
|
801
|
+
font: var(--card-thumb-font, var(--type-label));
|
|
802
802
|
opacity: 0.85;
|
|
803
803
|
}
|
|
804
804
|
|
|
805
805
|
/* ---- inline stat block (revenue-style) ---- */
|
|
806
|
-
.stat { display: grid; gap: var(--sp-1); }
|
|
807
|
-
.
|
|
806
|
+
.stat { display: grid; gap: var(--stat-gap, var(--sp-1)); }
|
|
807
|
+
.stat--centre { text-align: center; justify-items: center; } /* the one number of a sheet or a stage, under a centred avatar */
|
|
808
|
+
.stat__row { display: flex; align-items: center; justify-content: space-between; gap: var(--stat-row-gap, var(--sp-3)); }
|
|
808
809
|
|
|
809
810
|
.delta {
|
|
810
|
-
font: var(--type-caption);
|
|
811
|
+
font: var(--delta-font, var(--type-caption));
|
|
811
812
|
font-weight: 700;
|
|
812
|
-
padding: var(--sp-1) var(--sp-2);
|
|
813
|
-
border-radius: var(--r-chip);
|
|
814
|
-
background: var(--card-3-bg);
|
|
815
|
-
color: var(--card-3-ink);
|
|
813
|
+
padding: var(--delta-pad-y, var(--sp-1)) var(--delta-pad-x, var(--sp-2));
|
|
814
|
+
border-radius: var(--delta-radius, var(--r-chip));
|
|
815
|
+
background: var(--delta-bg, var(--card-3-bg));
|
|
816
|
+
color: var(--delta-ink, var(--card-3-ink));
|
|
816
817
|
}
|
|
817
|
-
.delta--neg { background: var(--neg); color: var(--card-3-ink); } /* light ink on the signal color in every slot */
|
|
818
|
+
.delta--neg { background: var(--delta-neg-bg, var(--neg)); color: var(--delta-neg-ink, var(--card-3-ink)); } /* light ink on the signal color in every slot */
|
|
818
819
|
|
|
819
820
|
/* ---- css/stack.css — sheet stack: .sheet-stack — the menu, sheets stacked, heads peek, tap opens ---- */
|
|
820
821
|
/* ============================================================
|
|
@@ -838,7 +839,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
838
839
|
<div class="sheet-stack sheet-stack--tap"> (drop --tap for a static stack)
|
|
839
840
|
<article class="card card--2">
|
|
840
841
|
<div class="card__head">
|
|
841
|
-
<button class="icon-btn icon-btn--sm
|
|
842
|
+
<button class="icon-btn icon-btn--sm sheet-stack__back" aria-label="Back">…</button>
|
|
842
843
|
<h2 class="t-h2 t-caps">Shopping</h2>
|
|
843
844
|
<span class="icon-row">…</span> (optional trailing icons)
|
|
844
845
|
</div>
|
|
@@ -1062,7 +1063,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1062
1063
|
.sheet-stack--tap > .card.is-open > .card__head {
|
|
1063
1064
|
padding-left: calc(var(--tap-sm) + var(--sp-3));
|
|
1064
1065
|
}
|
|
1065
|
-
.sheet-stack--tap .
|
|
1066
|
+
.sheet-stack--tap .sheet-stack__back {
|
|
1066
1067
|
position: absolute;
|
|
1067
1068
|
left: 0;
|
|
1068
1069
|
top: 50%;
|
|
@@ -1071,7 +1072,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1071
1072
|
visibility: hidden;
|
|
1072
1073
|
transition: opacity 0.35s ease, visibility 0.35s;
|
|
1073
1074
|
}
|
|
1074
|
-
.sheet-stack--tap > .card.is-open .
|
|
1075
|
+
.sheet-stack--tap > .card.is-open .sheet-stack__back {
|
|
1075
1076
|
opacity: 1;
|
|
1076
1077
|
visibility: visible;
|
|
1077
1078
|
}
|
|
@@ -1159,7 +1160,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1159
1160
|
overflow: clip;
|
|
1160
1161
|
border-radius: var(--r-card) var(--r-card) 0 0;
|
|
1161
1162
|
border-bottom: 0;
|
|
1162
|
-
box-shadow: var(--shadow-sheet);
|
|
1163
|
+
box-shadow: var(--sheet-shadow, var(--shadow-sheet));
|
|
1163
1164
|
}
|
|
1164
1165
|
.sheet--full { --_y: 0%; --_cover: 0rem; }
|
|
1165
1166
|
.sheet--half { --_y: calc(var(--sheet-half) * 100%); --_cover: calc(var(--sheet-half) * var(--_box)); }
|
|
@@ -1257,7 +1258,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1257
1258
|
opacity: 0.3;
|
|
1258
1259
|
width: var(--tap-sm);
|
|
1259
1260
|
height: var(--_handle);
|
|
1260
|
-
border-radius: var(--r-chip);
|
|
1261
|
+
border-radius: var(--sheet-handle-radius, var(--r-chip));
|
|
1261
1262
|
margin: 0 auto;
|
|
1262
1263
|
padding: 0;
|
|
1263
1264
|
touch-action: none;
|
|
@@ -1289,8 +1290,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1289
1290
|
min-height: var(--tap);
|
|
1290
1291
|
text-align: center;
|
|
1291
1292
|
}
|
|
1292
|
-
.sheet__title { font: var(--type-h2); letter-spacing: var(--track-h2); margin: 0; }
|
|
1293
|
-
.sheet__sub { font: var(--type-caption); color: var(--card-muted); }
|
|
1293
|
+
.sheet__title { font: var(--sheet-title-font, var(--type-h2)); letter-spacing: var(--track-h2); margin: 0; }
|
|
1294
|
+
.sheet__sub { font: var(--sheet-sub-font, var(--type-caption)); color: var(--sheet-sub-ink, var(--card-muted)); }
|
|
1294
1295
|
|
|
1295
1296
|
/* ============================================================
|
|
1296
1297
|
.sheet-stage — the overlay stage: a page-height box the sheet
|
|
@@ -1312,7 +1313,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1312
1313
|
margin-inline: auto;
|
|
1313
1314
|
overflow: clip; /* not hidden: a clipped box is not a scroll container, so revealing a focused field never shifts the stage */
|
|
1314
1315
|
container-type: size; /* the sheet's body reads this box's height as 100cqh (--_box above) */
|
|
1315
|
-
background: var(--surface);
|
|
1316
|
+
background: var(--sheet-stage-bg, var(--surface));
|
|
1316
1317
|
}
|
|
1317
1318
|
/* on an app screen the stage is the page: it takes the screen's inset back (edge to
|
|
1318
1319
|
edge, like a step) and, after a fixed .appbar, the room the screen reserved for
|
|
@@ -1329,8 +1330,13 @@ button { font: inherit; cursor: pointer; }
|
|
|
1329
1330
|
inset-inline: var(--screen-pad);
|
|
1330
1331
|
top: var(--_band-centre);
|
|
1331
1332
|
translate: 0 -50%;
|
|
1332
|
-
/*
|
|
1333
|
+
/* as tall as the band less the screen's inset at both ends (Lh 2026-09-22: the room under the hero = the room at its sides);
|
|
1334
|
+
what it holds fills it (a postcard's min-height gives way) */
|
|
1335
|
+
height: calc((100cqh - var(--_top, 0rem)) / 2 - 2 * var(--screen-pad));
|
|
1336
|
+
display: grid;
|
|
1333
1337
|
}
|
|
1338
|
+
.sheet-stage > .sheet-stage__hero > * { min-height: 0; } /* outranks a component's own min-height (the postcard's) */
|
|
1339
|
+
/* no z-index on the hero: it follows the background in DOM order and the sheet, later, covers it at full */
|
|
1334
1340
|
|
|
1335
1341
|
/* ---- map: the box a map engine draws in, behind a stage sheet ----
|
|
1336
1342
|
<div class="sheet-stage__bg"><div class="map"><div>…the engine's canvas…</div>
|
|
@@ -1347,7 +1353,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1347
1353
|
position: absolute;
|
|
1348
1354
|
inset: 0;
|
|
1349
1355
|
isolation: isolate; /* the ring and the hint stack inside the map only — a sheet over the map covers them */
|
|
1350
|
-
color: var(--on-surface);
|
|
1356
|
+
color: var(--map-ink, var(--on-surface));
|
|
1351
1357
|
background:
|
|
1352
1358
|
repeating-linear-gradient(75deg, transparent 0 5.5rem, var(--hairline) 5.5rem 6.25rem),
|
|
1353
1359
|
repeating-linear-gradient(-18deg, transparent 0 8.75rem, var(--ink-tint) 8.75rem 9.25rem),
|
|
@@ -1365,9 +1371,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
1365
1371
|
height: var(--map-ring);
|
|
1366
1372
|
translate: -50% -50%;
|
|
1367
1373
|
border-radius: 50%;
|
|
1368
|
-
border: var(--border-w) solid var(--accent);
|
|
1369
|
-
background: color-mix(in srgb, var(--accent) 22%, transparent);
|
|
1370
|
-
box-shadow: var(--shadow-float);
|
|
1374
|
+
border: var(--border-w) solid var(--map-ring-border, var(--accent));
|
|
1375
|
+
background: var(--map-ring-bg, color-mix(in srgb, var(--accent) 22%, transparent));
|
|
1376
|
+
box-shadow: var(--map-ring-shadow, var(--shadow-float));
|
|
1371
1377
|
pointer-events: none;
|
|
1372
1378
|
z-index: 1; /* above the engine's panes (Leaflet's tiles sit at 200, but inside their own stacking context) */
|
|
1373
1379
|
}
|
|
@@ -1376,10 +1382,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
1376
1382
|
right: var(--sp-2);
|
|
1377
1383
|
bottom: var(--sp-2);
|
|
1378
1384
|
padding: var(--sp-0) var(--sp-2);
|
|
1379
|
-
border-radius: var(--r-chip);
|
|
1380
|
-
background: color-mix(in srgb, var(--surface) 80%, transparent);
|
|
1381
|
-
color: var(--on-surface-muted);
|
|
1382
|
-
font: var(--type-caption);
|
|
1385
|
+
border-radius: var(--map-credit-radius, var(--r-chip));
|
|
1386
|
+
background: var(--map-credit-bg, color-mix(in srgb, var(--surface) 80%, transparent));
|
|
1387
|
+
color: var(--map-credit-ink, var(--on-surface-muted));
|
|
1388
|
+
font: var(--map-credit-font, var(--type-caption));
|
|
1383
1389
|
pointer-events: none;
|
|
1384
1390
|
z-index: 1;
|
|
1385
1391
|
}
|
|
@@ -1390,12 +1396,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
1390
1396
|
translate: -50% 0;
|
|
1391
1397
|
max-width: calc(100% - 2 * var(--sp-4));
|
|
1392
1398
|
padding: var(--sp-2) var(--sp-4);
|
|
1393
|
-
border-radius: var(--r-chip);
|
|
1394
|
-
background: var(--card-3-bg);
|
|
1395
|
-
color: var(--card-3-ink);
|
|
1396
|
-
font: var(--type-label);
|
|
1399
|
+
border-radius: var(--map-hint-radius, var(--r-chip));
|
|
1400
|
+
background: var(--map-hint-bg, var(--card-3-bg));
|
|
1401
|
+
color: var(--map-hint-ink, var(--card-3-ink));
|
|
1402
|
+
font: var(--map-hint-font, var(--type-label));
|
|
1397
1403
|
text-align: center;
|
|
1398
|
-
box-shadow: var(--shadow-float);
|
|
1404
|
+
box-shadow: var(--map-hint-shadow, var(--shadow-float));
|
|
1399
1405
|
pointer-events: none;
|
|
1400
1406
|
z-index: 1;
|
|
1401
1407
|
}
|
|
@@ -1406,10 +1412,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
1406
1412
|
/* EVERY sheet has one shape (Lh, 2026-09-21): the handle and the head stay at the top and never scroll · the
|
|
1407
1413
|
foot's button stands at the bottom, and ABOVE the keyboard when there is one · everything between them
|
|
1408
1414
|
scrolls, as far as it needs to. In a .step the keyboard's room is the step's (--kb, step.css); on a stage
|
|
1409
|
-
the sheet gives it up itself, as --_lift
|
|
1415
|
+
the sheet gives it up itself, as --_lift. (A sheet ALWAYS stands on the floor — `.sheet--raised`, the one that
|
|
1416
|
+
floated above a footnote strip on a dim stage, was retired 2026-09-22: that is a Modal's job.) */
|
|
1410
1417
|
.sheet-stage > .sheet { --_lift: var(--kb, 0rem); bottom: var(--_lift); }
|
|
1411
|
-
.sheet-stage > .sheet--raised { --_lift: calc(var(--tap-lg) + var(--kb, 0rem)); }
|
|
1412
|
-
.sheet--raised { --_lift: var(--tap-lg); bottom: var(--_lift); border-radius: var(--r-card); border-bottom: var(--border-w) solid var(--card-border); }
|
|
1413
1418
|
.sheet-stage__foot {
|
|
1414
1419
|
position: absolute;
|
|
1415
1420
|
inset-inline: 0;
|
|
@@ -1418,8 +1423,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1418
1423
|
margin: 0;
|
|
1419
1424
|
display: grid;
|
|
1420
1425
|
place-items: center;
|
|
1421
|
-
font: var(--type-caption);
|
|
1422
|
-
color: var(--card-3-ink);
|
|
1426
|
+
font: var(--sheet-stage-foot-font, var(--type-caption));
|
|
1427
|
+
color: var(--sheet-stage-foot-ink, var(--card-3-ink));
|
|
1423
1428
|
z-index: 2;
|
|
1424
1429
|
}
|
|
1425
1430
|
|
|
@@ -1543,13 +1548,13 @@ button { font: inherit; cursor: pointer; }
|
|
|
1543
1548
|
.field {
|
|
1544
1549
|
display: flex;
|
|
1545
1550
|
align-items: center;
|
|
1546
|
-
gap: var(--sp-2);
|
|
1547
|
-
border: var(--border-w) solid var(--action-border);
|
|
1548
|
-
border-radius: var(--r-control);
|
|
1549
|
-
background: var(--action-bg);
|
|
1550
|
-
color: var(--action-ink);
|
|
1551
|
-
padding-inline: var(--sp-4);
|
|
1552
|
-
min-height: var(--tap);
|
|
1551
|
+
gap: var(--field-gap, var(--sp-2));
|
|
1552
|
+
border: var(--border-w) solid var(--field-border, var(--action-border));
|
|
1553
|
+
border-radius: var(--field-radius, var(--r-control));
|
|
1554
|
+
background: var(--field-bg, var(--action-bg));
|
|
1555
|
+
color: var(--field-ink, var(--action-ink));
|
|
1556
|
+
padding-inline: var(--field-pad-x, var(--sp-4));
|
|
1557
|
+
min-height: var(--field-min-h, var(--tap));
|
|
1553
1558
|
min-width: 0;
|
|
1554
1559
|
}
|
|
1555
1560
|
|
|
@@ -1566,7 +1571,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
1566
1571
|
.field input::placeholder { color: inherit; opacity: 0.55; }
|
|
1567
1572
|
|
|
1568
1573
|
/* field with its action inside: date pick, send, go — the button sits flush right */
|
|
1569
|
-
.field--action { padding-right: var(--sp-1); min-height: var(--tap-lg); }
|
|
1574
|
+
.field--action { padding-right: var(--sp-1); min-height: var(--field-action-min-h, var(--tap-lg)); }
|
|
1570
1575
|
.field--action > .icon-btn { margin-left: auto; }
|
|
1571
1576
|
|
|
1572
1577
|
/* colored palettes drop the visible outline elsewhere — fields keep one */
|
|
@@ -1576,25 +1581,25 @@ button { font: inherit; cursor: pointer; }
|
|
|
1576
1581
|
.add-row {
|
|
1577
1582
|
display: inline-flex;
|
|
1578
1583
|
align-items: center;
|
|
1579
|
-
gap: var(--sp-2);
|
|
1584
|
+
gap: var(--add-row-gap, var(--sp-2));
|
|
1580
1585
|
border: 0;
|
|
1581
1586
|
background: none;
|
|
1582
1587
|
color: inherit;
|
|
1583
|
-
font: var(--type-label);
|
|
1584
|
-
font-size: var(--fs-body);
|
|
1585
|
-
padding: var(--sp-2) 0;
|
|
1586
|
-
min-height: var(--tap-sm);
|
|
1588
|
+
font: var(--add-row-font, var(--type-label));
|
|
1589
|
+
font-size: var(--add-row-font-size, var(--fs-body));
|
|
1590
|
+
padding: var(--add-row-pad-y, var(--sp-2)) var(--add-row-pad-x, 0);
|
|
1591
|
+
min-height: var(--add-row-min-h, var(--tap-sm));
|
|
1587
1592
|
}
|
|
1588
1593
|
.add-row__plus {
|
|
1589
|
-
width: var(--icon);
|
|
1590
|
-
height: var(--icon);
|
|
1594
|
+
width: var(--add-row-plus-size, var(--icon));
|
|
1595
|
+
height: var(--add-row-plus-size, var(--icon));
|
|
1591
1596
|
flex: none;
|
|
1592
1597
|
border-radius: 50%;
|
|
1593
|
-
background: var(--accent);
|
|
1594
|
-
color: var(--on-accent);
|
|
1598
|
+
background: var(--add-row-plus-bg, var(--accent));
|
|
1599
|
+
color: var(--add-row-plus-ink, var(--on-accent));
|
|
1595
1600
|
display: grid;
|
|
1596
1601
|
place-items: center;
|
|
1597
|
-
font-size: var(--fs-md);
|
|
1602
|
+
font-size: var(--add-row-plus-font-size, var(--fs-md));
|
|
1598
1603
|
line-height: 1;
|
|
1599
1604
|
}
|
|
1600
1605
|
.add-row__plus .icon { width: var(--icon-xs); height: var(--icon-xs); }
|
|
@@ -1607,40 +1612,40 @@ button { font: inherit; cursor: pointer; }
|
|
|
1607
1612
|
============================================================ */
|
|
1608
1613
|
|
|
1609
1614
|
/* ---- route: endpoint · dotted connector (icon in middle) · endpoint ---- */
|
|
1610
|
-
.route { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; column-gap: var(--sp-1); } /* line → icon: --sp-1, as the via always had; code → line is the code row's own --sp-3 */
|
|
1615
|
+
.route { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; column-gap: var(--route-col-gap, var(--sp-1)); } /* line → icon: --sp-1, as the via always had; code → line is the code row's own --sp-3 */
|
|
1611
1616
|
/* the two endpoints get EQUAL columns whatever their words measure, so the via
|
|
1612
1617
|
(the icon alone) sits at the card's centre; each dotted run lives on ITS
|
|
1613
1618
|
endpoint's code row and fills that side's leftover — with short names the
|
|
1614
1619
|
runs are long and equal, a long place name wraps in its half instead of
|
|
1615
1620
|
pushing the plane (Lh 2026-09-16) */
|
|
1616
1621
|
|
|
1617
|
-
.route__point { display: grid; gap: var(--sp-0); min-width: 0; }
|
|
1622
|
+
.route__point { display: grid; gap: var(--route-point-gap, var(--sp-0)); min-width: 0; }
|
|
1618
1623
|
.route__point--end { text-align: right; }
|
|
1619
1624
|
|
|
1620
|
-
.route__code { font: var(--type-h2); letter-spacing: var(--track-h2); display: flex; align-items: center; gap: var(--sp-3); }
|
|
1625
|
+
.route__code { font: var(--route-code-font, var(--type-h2)); letter-spacing: var(--track-h2); display: flex; align-items: center; gap: var(--route-code-gap, var(--sp-3)); }
|
|
1621
1626
|
.route__code::after, .route__point--end .route__code::before { content: ""; flex: 1; border-top: var(--rule-w) dotted currentColor; opacity: 0.65; }
|
|
1622
1627
|
.route__point--end .route__code::after { content: none; }
|
|
1623
|
-
.route__time { font: var(--type-body); font-variant-numeric: tabular-nums; }
|
|
1624
|
-
.route__place { font: var(--type-caption); color: var(--card-muted); }
|
|
1628
|
+
.route__time { font: var(--route-time-font, var(--type-body)); font-variant-numeric: tabular-nums; }
|
|
1629
|
+
.route__place { font: var(--route-place-font, var(--type-caption)); color: var(--route-place-ink, var(--card-muted)); }
|
|
1625
1630
|
.card--3 .route__place { color: inherit; opacity: 0.7; }
|
|
1626
1631
|
|
|
1627
|
-
.route__via { display: flex; align-items: center; opacity: 0.65; font-size: var(--fs-body); }
|
|
1632
|
+
.route__via { display: flex; align-items: center; opacity: 0.65; font-size: var(--route-via-font-size, var(--fs-body)); }
|
|
1628
1633
|
|
|
1629
1634
|
/* ---- tile badge: tiny label over a big value (gate, seat, zone) ---- */
|
|
1630
1635
|
.tile-badge {
|
|
1631
1636
|
display: inline-grid;
|
|
1632
1637
|
justify-items: center;
|
|
1633
|
-
gap: var(--sp-0);
|
|
1634
|
-
padding: var(--sp-1) var(--sp-2);
|
|
1635
|
-
border-radius: var(--r-badge);
|
|
1636
|
-
background: var(--card-3-bg);
|
|
1637
|
-
color: var(--card-3-ink);
|
|
1638
|
+
gap: var(--tile-badge-gap, var(--sp-0));
|
|
1639
|
+
padding: var(--tile-badge-pad-y, var(--sp-1)) var(--tile-badge-pad-x, var(--sp-2));
|
|
1640
|
+
border-radius: var(--tile-badge-radius, var(--r-badge));
|
|
1641
|
+
background: var(--tile-badge-bg, var(--card-3-bg));
|
|
1642
|
+
color: var(--tile-badge-ink, var(--card-3-ink));
|
|
1638
1643
|
}
|
|
1639
|
-
.tile-badge__label { font: var(--type-overline); letter-spacing: var(--track-overline); text-transform: uppercase; opacity: 0.7; }
|
|
1644
|
+
.tile-badge__label { font: var(--tile-badge-label-font, var(--type-overline)); letter-spacing: var(--track-overline); text-transform: uppercase; opacity: 0.7; }
|
|
1640
1645
|
.tile-badge__value { font: 800 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
|
|
1641
1646
|
|
|
1642
1647
|
/* plain: no fill — a date box beside a title (Jun / 18) */
|
|
1643
|
-
.tile-badge--plain { background: transparent; color: inherit; padding: 0; min-width: var(--tap-sm); }
|
|
1648
|
+
.tile-badge--plain { background: transparent; color: inherit; padding: 0; min-width: var(--tile-badge-plain-min-w, var(--tap-sm)); }
|
|
1644
1649
|
.tile-badge--plain .tile-badge__value { font: 800 var(--fs-h1)/1 var(--font-display); }
|
|
1645
1650
|
|
|
1646
1651
|
/* corner badge: pin a tile badge on media (rating, price) — .card__media is relative */
|
|
@@ -1650,9 +1655,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
1650
1655
|
.note-row {
|
|
1651
1656
|
display: flex;
|
|
1652
1657
|
align-items: center;
|
|
1653
|
-
gap: var(--sp-2);
|
|
1654
|
-
font: var(--type-caption);
|
|
1655
|
-
color: var(--card-muted);
|
|
1658
|
+
gap: var(--note-row-gap, var(--sp-2));
|
|
1659
|
+
font: var(--note-row-font, var(--type-caption));
|
|
1660
|
+
color: var(--note-row-ink, var(--card-muted));
|
|
1656
1661
|
}
|
|
1657
1662
|
.card--3 .note-row { color: inherit; opacity: 0.7; }
|
|
1658
1663
|
|
|
@@ -1662,6 +1667,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
1662
1667
|
topbar, chips, buttons, selects, bottom CTA pack
|
|
1663
1668
|
============================================================ */
|
|
1664
1669
|
|
|
1670
|
+
/* KNOBS (Lh 2026-09-21) — cardds is a framework: a project adjusts ONE component from outside, without touching this file.
|
|
1671
|
+
A knob is a custom property a rule reads WITH ITS DEFAULT AS THE FALLBACK — `padding-inline: var(--btn-pad-x, var(--sp-6))` —
|
|
1672
|
+
so it is declared nowhere: unset, nothing moves (tests/style-snapshot proves it); set in the project's theme.css it retunes
|
|
1673
|
+
that component everywhere; set on any element it retunes the components inside that element only. Names: --<component>-<what>
|
|
1674
|
+
(--btn-pad-x, --chip-bg, --icon-btn-size, --appbar-pad-x); a variant says so (--btn-primary-bg, --chip-on-ink, --icon-btn-sm-size).
|
|
1675
|
+
The sizes that already were root tokens (--btn-h, --chip-h, --bar-h, --header-h, --fab…) are knobs too — set them the same way.
|
|
1676
|
+
Prefer a BASE when the whole system should move (tokens.css); reach for a knob when ONE component should. `npm run knobs` lists them all. */
|
|
1677
|
+
|
|
1665
1678
|
/* ---- the bars across the top of a screen: one height, whatever they carry ----
|
|
1666
1679
|
.topbar (screen title) and .appbar (app context) are the same bar with
|
|
1667
1680
|
different contents, so the height is declared here once rather than left to
|
|
@@ -1672,8 +1685,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1672
1685
|
.topbar, .appbar {
|
|
1673
1686
|
display: flex;
|
|
1674
1687
|
align-items: center;
|
|
1675
|
-
gap: var(--sp-3);
|
|
1676
|
-
padding-block: var(--sp-2);
|
|
1688
|
+
gap: var(--bar-gap, var(--sp-3));
|
|
1689
|
+
padding-block: var(--bar-pad-y, var(--sp-2));
|
|
1677
1690
|
min-height: var(--header-h);
|
|
1678
1691
|
}
|
|
1679
1692
|
|
|
@@ -1702,9 +1715,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
1702
1715
|
top: 0;
|
|
1703
1716
|
max-width: var(--screen-w);
|
|
1704
1717
|
margin-inline: auto;
|
|
1705
|
-
padding-inline: 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) */
|
|
1718
|
+
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) */
|
|
1706
1719
|
z-index: 40; /* topmost — card animations never cover it */
|
|
1707
|
-
background: var(--surface); /* solid: sliding cards never show through */
|
|
1720
|
+
background: var(--appbar-bg, var(--surface)); /* solid: sliding cards never show through */
|
|
1708
1721
|
}
|
|
1709
1722
|
/* fixed, so it covers the top of the screen it sits in — reserve its height
|
|
1710
1723
|
there, from the same token, the way the bottom bars reserve theirs. The
|
|
@@ -1721,19 +1734,19 @@ button { font: inherit; cursor: pointer; }
|
|
|
1721
1734
|
|
|
1722
1735
|
/* ---- icon button (round) ---- */
|
|
1723
1736
|
.icon-btn {
|
|
1724
|
-
width: var(--tap); height: var(--tap);
|
|
1737
|
+
width: var(--icon-btn-size, var(--tap)); height: var(--icon-btn-size, var(--tap));
|
|
1725
1738
|
flex: none;
|
|
1726
|
-
border-radius: var(--r-chip);
|
|
1727
|
-
border: var(--border-w) solid var(--outline); /* see .btn — same reason */
|
|
1728
|
-
background: var(--action-bg);
|
|
1729
|
-
color: var(--action-ink);
|
|
1739
|
+
border-radius: var(--icon-btn-radius, var(--r-chip));
|
|
1740
|
+
border: var(--border-w) solid var(--icon-btn-border, var(--outline)); /* see .btn — same reason */
|
|
1741
|
+
background: var(--icon-btn-bg, var(--action-bg));
|
|
1742
|
+
color: var(--icon-btn-ink, var(--action-ink));
|
|
1730
1743
|
display: grid;
|
|
1731
1744
|
place-items: center;
|
|
1732
1745
|
font-size: var(--fs-lg);
|
|
1733
1746
|
line-height: 1;
|
|
1734
1747
|
}
|
|
1735
|
-
.icon-btn--invert { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
1736
|
-
.icon-btn--sm { width: var(--tap-sm); height: var(--tap-sm); font-size: var(--fs-title); }
|
|
1748
|
+
.icon-btn--invert { background: var(--icon-btn-invert-bg, var(--accent)); color: var(--icon-btn-invert-ink, var(--on-accent)); border-color: transparent; }
|
|
1749
|
+
.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); }
|
|
1737
1750
|
/* fab: the square floating action, bottom-right of a stage or screen */
|
|
1738
1751
|
.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: 31; }
|
|
1739
1752
|
|
|
@@ -1742,21 +1755,21 @@ button { font: inherit; cursor: pointer; }
|
|
|
1742
1755
|
/* --outline, not --action-border: same reason as .chip — on a palette where
|
|
1743
1756
|
--card-1-bg equals --action-bg a transparent border makes a plain button
|
|
1744
1757
|
invisible on a white card. --primary and --quiet set their own edge. */
|
|
1745
|
-
border: var(--border-w) solid var(--outline);
|
|
1746
|
-
background: var(--action-bg);
|
|
1747
|
-
color: var(--action-ink);
|
|
1748
|
-
border-radius: var(--r-chip);
|
|
1749
|
-
padding: var(--sp-4) var(--sp-6);
|
|
1758
|
+
border: var(--border-w) solid var(--btn-border, var(--outline));
|
|
1759
|
+
background: var(--btn-bg, var(--action-bg));
|
|
1760
|
+
color: var(--btn-ink, var(--action-ink));
|
|
1761
|
+
border-radius: var(--btn-radius, var(--r-chip));
|
|
1762
|
+
padding: var(--btn-pad-y, var(--sp-4)) var(--btn-pad-x, var(--sp-6));
|
|
1750
1763
|
font: var(--type-label);
|
|
1751
|
-
font-size: var(--fs-md);
|
|
1764
|
+
font-size: var(--btn-font-size, var(--fs-md));
|
|
1752
1765
|
display: inline-flex;
|
|
1753
1766
|
align-items: center;
|
|
1754
1767
|
justify-content: center;
|
|
1755
|
-
gap: var(--sp-2);
|
|
1768
|
+
gap: var(--btn-gap, var(--sp-2));
|
|
1756
1769
|
min-height: var(--btn-h);
|
|
1757
1770
|
text-decoration: none; /* .btn works as <a> too */
|
|
1758
1771
|
}
|
|
1759
|
-
.btn--primary { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
1772
|
+
.btn--primary { background: var(--btn-primary-bg, var(--accent)); color: var(--btn-primary-ink, var(--on-accent)); border-color: transparent; }
|
|
1760
1773
|
/* quiet keeps a faint outline so it still reads as a button on colored palettes;
|
|
1761
1774
|
it is ink on whatever it sits on — inherit, or on the dark slot (card--3) it
|
|
1762
1775
|
would take the light-card action ink and vanish */
|
|
@@ -1776,25 +1789,25 @@ button { font: inherit; cursor: pointer; }
|
|
|
1776
1789
|
.chip {
|
|
1777
1790
|
display: inline-flex;
|
|
1778
1791
|
align-items: center;
|
|
1779
|
-
gap: var(--sp-2);
|
|
1780
|
-
border-radius: var(--r-chip);
|
|
1792
|
+
gap: var(--chip-gap, var(--sp-2));
|
|
1793
|
+
border-radius: var(--chip-radius, var(--r-chip));
|
|
1781
1794
|
/* --outline, not --action-border: a chip is a control, and on a palette
|
|
1782
1795
|
where --card-1-bg == --action-bg (field, roast, hydro) a transparent
|
|
1783
1796
|
border makes it invisible on a white card. Same edge fields, checks,
|
|
1784
1797
|
toggles, tiles and the dropdown already use. */
|
|
1785
|
-
border: var(--border-w) solid var(--outline);
|
|
1786
|
-
background: var(--action-bg);
|
|
1787
|
-
color: var(--action-ink);
|
|
1788
|
-
padding: var(--sp-2) var(--sp-3);
|
|
1798
|
+
border: var(--border-w) solid var(--chip-border, var(--outline));
|
|
1799
|
+
background: var(--chip-bg, var(--action-bg));
|
|
1800
|
+
color: var(--chip-ink, var(--action-ink));
|
|
1801
|
+
padding: var(--chip-pad-y, var(--sp-2)) var(--chip-pad-x, var(--sp-3));
|
|
1789
1802
|
font: var(--type-label);
|
|
1790
|
-
font-size: var(--fs-sm);
|
|
1803
|
+
font-size: var(--chip-font-size, var(--fs-sm));
|
|
1791
1804
|
min-height: var(--chip-h);
|
|
1792
1805
|
text-decoration: none;
|
|
1793
1806
|
justify-self: start; /* as a grid child it hugs its text, never stretches */
|
|
1794
1807
|
}
|
|
1795
1808
|
.dot { width: var(--dot); height: var(--dot); flex: none; border-radius: 50%; background: var(--dot-a); } /* signal dot: chips, legends */
|
|
1796
1809
|
.dot--b { background: var(--dot-b); }
|
|
1797
|
-
.chip--on { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
1810
|
+
.chip--on { background: var(--chip-on-bg, var(--accent)); color: var(--chip-on-ink, var(--on-accent)); border-color: transparent; }
|
|
1798
1811
|
/* dark-slot inversion lives in card.css (.card--3 :is(...)) */
|
|
1799
1812
|
|
|
1800
1813
|
.chip-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
|
|
@@ -1930,6 +1943,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
1930
1943
|
|
|
1931
1944
|
/* the first round button sits at the left edge, the last at the right; a skip link between them stays centred */
|
|
1932
1945
|
:is(.action-bar--pager, .action-bar__tier--pager) > .icon-btn:first-child { margin-right: auto; }
|
|
1946
|
+
/* …and whatever goes BETWEEN the two (a "skip" link) stands in the middle: the bar places it, never the link itself */
|
|
1947
|
+
:is(.action-bar--pager, .action-bar__tier--pager) > :not(.icon-btn):not(:first-child):not(:last-child) { margin-inline: auto; }
|
|
1948
|
+
:is(.action-bar--pager, .action-bar__tier--pager):has(> :nth-child(3)) > .icon-btn:first-child { margin-right: 0; }
|
|
1933
1949
|
|
|
1934
1950
|
/* .screen's default bottom padding is a constant; derive the reserve from the
|
|
1935
1951
|
bar instead, so a taller button (a longer label, a script with more leading)
|
|
@@ -1973,8 +1989,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
1973
1989
|
mask: radial-gradient(farthest-side, transparent calc(100% - var(--_w) - 1px), currentColor calc(100% - var(--_w)));
|
|
1974
1990
|
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - var(--_w) - 1px), currentColor calc(100% - var(--_w)));
|
|
1975
1991
|
}
|
|
1976
|
-
.ring__value { font: var(--type-stat); letter-spacing: var(--track-display); }
|
|
1977
|
-
.ring__label { font: var(--type-caption); color: var(--card-muted); }
|
|
1992
|
+
.ring__value { font: var(--ring-value-font, var(--type-stat)); letter-spacing: var(--track-display); }
|
|
1993
|
+
.ring__label { font: var(--ring-label-font, var(--type-caption)); color: var(--ring-label-ink, var(--card-muted)); }
|
|
1978
1994
|
.card--3 .ring__label { color: inherit; opacity: 0.7; }
|
|
1979
1995
|
.ring--sm { --_size: var(--avatar-xl); --_w: var(--sp-3); }
|
|
1980
1996
|
.ring--sm .ring__value { font: 800 var(--fs-2xl)/1 var(--font-display); }
|
|
@@ -1994,10 +2010,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
1994
2010
|
--on-accent: var(--_bg);
|
|
1995
2011
|
list-style: none;
|
|
1996
2012
|
margin: 0;
|
|
1997
|
-
padding: var(--sp-2);
|
|
2013
|
+
padding: var(--track-pad, var(--sp-2));
|
|
1998
2014
|
display: flex;
|
|
1999
2015
|
align-items: center;
|
|
2000
|
-
border-radius: var(--r-chip);
|
|
2016
|
+
border-radius: var(--track-radius, var(--r-chip));
|
|
2001
2017
|
background: var(--_bg);
|
|
2002
2018
|
color: var(--_ink);
|
|
2003
2019
|
}
|
|
@@ -2038,9 +2054,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
2038
2054
|
grid-template-areas: "label num" "foot num";
|
|
2039
2055
|
align-items: end;
|
|
2040
2056
|
gap: var(--sp-2) var(--sp-4);
|
|
2041
|
-
min-height: var(--tile-h);
|
|
2057
|
+
min-height: var(--card-band-min-h, var(--tile-h));
|
|
2042
2058
|
}
|
|
2043
|
-
.band__label { grid-area: label; align-self: start; font: var(--type-label); font-size: var(--fs-body); line-height: 1.2; }
|
|
2059
|
+
.band__label { grid-area: label; align-self: start; font: var(--band-label-font, var(--type-label)); font-size: var(--band-label-font-size, var(--fs-body)); line-height: 1.2; }
|
|
2044
2060
|
.band__num {
|
|
2045
2061
|
grid-area: num;
|
|
2046
2062
|
font: 800 var(--fs-giant) / 0.9 var(--font-display);
|
|
@@ -2048,14 +2064,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
2048
2064
|
font-variant-numeric: tabular-nums;
|
|
2049
2065
|
}
|
|
2050
2066
|
.band__num small { font-size: 0.5em; font-weight: 700; margin-right: 0.1em; } /* unit or % */
|
|
2051
|
-
.band__foot { grid-area: foot; font: var(--type-caption); color: var(--card-muted); }
|
|
2067
|
+
.band__foot { grid-area: foot; font: var(--band-foot-font, var(--type-caption)); color: var(--band-foot-ink, var(--card-muted)); }
|
|
2052
2068
|
.card--3 .band__foot { color: inherit; opacity: 0.7; }
|
|
2053
2069
|
/* band stack: ONE card boundary; bands are frameless inside it, a hairline
|
|
2054
2070
|
between each. Bands may still carry slot tints (card--2 / card--3). */
|
|
2055
2071
|
.band-stack {
|
|
2056
2072
|
display: grid;
|
|
2057
|
-
border: var(--border-w) solid var(--card-border);
|
|
2058
|
-
border-radius: var(--r-card);
|
|
2073
|
+
border: var(--border-w) solid var(--band-stack-border, var(--card-border));
|
|
2074
|
+
border-radius: var(--band-stack-radius, var(--r-card));
|
|
2059
2075
|
overflow: hidden;
|
|
2060
2076
|
}
|
|
2061
2077
|
.band-stack > .card--band { border: 0; border-radius: 0; box-shadow: none; }
|
|
@@ -2106,7 +2122,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2106
2122
|
|
|
2107
2123
|
/* ---- picker: horizontal snap picker, centre value big ----
|
|
2108
2124
|
<div class="picker"><div class="picker__rail"><button class="picker__item">100</button>…</div><span class="picker__unit">ml</span></div> */
|
|
2109
|
-
.picker { display: grid; gap: var(--sp-1); justify-items: center; position: relative; }
|
|
2125
|
+
.picker { display: grid; gap: var(--picker-gap, var(--sp-1)); justify-items: center; position: relative; }
|
|
2110
2126
|
.picker__rail {
|
|
2111
2127
|
width: 100%;
|
|
2112
2128
|
display: grid;
|
|
@@ -2114,7 +2130,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2114
2130
|
/* side padding = one third of the rail, so the first and last value can sit
|
|
2115
2131
|
in the centre; the content box is then one third wide, and each column
|
|
2116
2132
|
fills it (percent columns resolve against the content box) */
|
|
2117
|
-
padding-inline: calc(100% / 3);
|
|
2133
|
+
padding-inline: var(--picker-rail-pad-x, calc(100% / 3));
|
|
2118
2134
|
grid-auto-columns: 100%;
|
|
2119
2135
|
overflow-x: auto;
|
|
2120
2136
|
scroll-snap-type: x mandatory;
|
|
@@ -2127,8 +2143,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2127
2143
|
border: 0;
|
|
2128
2144
|
background: transparent;
|
|
2129
2145
|
color: inherit;
|
|
2130
|
-
padding: var(--sp-2) 0;
|
|
2131
|
-
font: var(--type-stat);
|
|
2146
|
+
padding: var(--picker-item-pad-y, var(--sp-2)) var(--picker-item-pad-x, 0);
|
|
2147
|
+
font: var(--picker-item-font, var(--type-stat));
|
|
2132
2148
|
letter-spacing: var(--track-display);
|
|
2133
2149
|
font-variant-numeric: tabular-nums;
|
|
2134
2150
|
opacity: 0.3;
|
|
@@ -2147,7 +2163,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2147
2163
|
left: calc(100% / 3);
|
|
2148
2164
|
}
|
|
2149
2165
|
.picker::after { left: auto; right: calc(100% / 3); }
|
|
2150
|
-
.picker__unit { font: var(--type-caption); color: var(--card-muted); }
|
|
2166
|
+
.picker__unit { font: var(--picker-unit-font, var(--type-caption)); color: var(--picker-unit-ink, var(--card-muted)); }
|
|
2151
2167
|
.card--3 .picker__unit { color: inherit; opacity: 0.7; }
|
|
2152
2168
|
/* the scrolled-to item lights up by position — no JS */
|
|
2153
2169
|
@supports (animation-timeline: view(x)) {
|
|
@@ -2163,30 +2179,30 @@ button { font: inherit; cursor: pointer; }
|
|
|
2163
2179
|
.badge {
|
|
2164
2180
|
display: inline-grid;
|
|
2165
2181
|
place-items: center;
|
|
2166
|
-
min-width: var(--badge);
|
|
2167
|
-
height: var(--badge);
|
|
2168
|
-
padding: 0 var(--sp-2);
|
|
2169
|
-
border-radius: var(--r-chip);
|
|
2170
|
-
background: var(--accent);
|
|
2171
|
-
color: var(--on-accent);
|
|
2172
|
-
font: var(--type-label);
|
|
2182
|
+
min-width: var(--badge-min-w, var(--badge));
|
|
2183
|
+
height: var(--badge-h, var(--badge));
|
|
2184
|
+
padding: var(--badge-pad-y, 0) var(--badge-pad-x, var(--sp-2));
|
|
2185
|
+
border-radius: var(--badge-radius, var(--r-chip));
|
|
2186
|
+
background: var(--badge-bg, var(--accent));
|
|
2187
|
+
color: var(--badge-ink, var(--on-accent));
|
|
2188
|
+
font: var(--badge-font, var(--type-label));
|
|
2173
2189
|
font-variant-numeric: tabular-nums;
|
|
2174
2190
|
vertical-align: middle;
|
|
2175
2191
|
}
|
|
2176
|
-
.badge--soft { background: var(--ink-tint); color: inherit; }
|
|
2192
|
+
.badge--soft { background: var(--badge-soft-bg, var(--ink-tint)); color: inherit; }
|
|
2177
2193
|
|
|
2178
2194
|
/* ---- bars: three or four bars with a count on top — the glance chart ----
|
|
2179
2195
|
<div class="bars"><div class="bar" style="--v: 16"><span>+16</span></div>… --max on the parent */
|
|
2180
|
-
.bars { --max: 20; --_h: 5rem; display: flex; align-items: end; gap: var(--sp-2); height: calc(var(--_h) + var(--sp-8)); }
|
|
2196
|
+
.bars { --max: 20; --_h: 5rem; display: flex; align-items: end; gap: var(--bars-gap, var(--sp-2)); height: calc(var(--_h) + var(--sp-8)); }
|
|
2181
2197
|
.bar {
|
|
2182
2198
|
flex: 1;
|
|
2183
2199
|
min-width: 0;
|
|
2184
2200
|
display: grid;
|
|
2185
2201
|
align-content: end;
|
|
2186
2202
|
justify-items: center;
|
|
2187
|
-
gap: var(--sp-1);
|
|
2203
|
+
gap: var(--bar-gap, var(--sp-1));
|
|
2188
2204
|
height: 100%;
|
|
2189
|
-
font: var(--type-caption);
|
|
2205
|
+
font: var(--bar-font, var(--type-caption));
|
|
2190
2206
|
font-weight: 700;
|
|
2191
2207
|
}
|
|
2192
2208
|
.bar::after {
|
|
@@ -2199,6 +2215,22 @@ button { font: inherit; cursor: pointer; }
|
|
|
2199
2215
|
.bar--on::after { background: var(--accent); }
|
|
2200
2216
|
.bar--on > span { background: var(--accent); color: var(--on-accent); padding: var(--sp-0) var(--sp-2); border-radius: var(--r-chip); }
|
|
2201
2217
|
|
|
2218
|
+
/* ---- progress: one straight line, the done share solid, the rest faint (Lh 2026-09-22) — a voice note's
|
|
2219
|
+
timeline, a download, a step count as a line. <div class="progress" role="progressbar" style="--progress-at: 0.4"></div>
|
|
2220
|
+
--progress-at is DATA the component writes (0–1), not a knob (scripts/knobs.mjs lists it). The done part is a
|
|
2221
|
+
gradient of the line's ink sized by the share; the rest is the background colour. */
|
|
2222
|
+
.progress {
|
|
2223
|
+
display: block;
|
|
2224
|
+
width: 100%;
|
|
2225
|
+
height: var(--progress-h, var(--sp-1));
|
|
2226
|
+
border-radius: var(--progress-radius, var(--r-chip));
|
|
2227
|
+
color: var(--progress-done-ink, inherit);
|
|
2228
|
+
background-color: var(--progress-rest-bg, color-mix(in srgb, currentColor 25%, transparent));
|
|
2229
|
+
background-image: linear-gradient(currentColor, currentColor);
|
|
2230
|
+
background-size: calc(var(--progress-at, 0) * 100%) 100%;
|
|
2231
|
+
background-repeat: no-repeat;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2202
2234
|
/* ---- css/people.css — avatar, avatar stack, chooser row ---- */
|
|
2203
2235
|
/* ============================================================
|
|
2204
2236
|
cardds/people.css — avatars: single, stacked, chooser row.
|
|
@@ -2217,9 +2249,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
2217
2249
|
border-radius: 50%;
|
|
2218
2250
|
display: grid;
|
|
2219
2251
|
place-items: center;
|
|
2220
|
-
background: var(--card-2-bg);
|
|
2221
|
-
color: var(--card-2-ink);
|
|
2222
|
-
font: var(--type-label);
|
|
2252
|
+
background: var(--avatar-bg, var(--card-2-bg));
|
|
2253
|
+
color: var(--avatar-ink, var(--card-2-ink));
|
|
2254
|
+
font: var(--avatar-font, var(--type-label));
|
|
2223
2255
|
font-size: calc(var(--_size) * 0.3);
|
|
2224
2256
|
text-transform: uppercase;
|
|
2225
2257
|
letter-spacing: 0.02em;
|
|
@@ -2232,10 +2264,10 @@ button { font: inherit; cursor: pointer; }
|
|
|
2232
2264
|
.avatar--lg { --_size: var(--avatar-lg); }
|
|
2233
2265
|
.avatar--xl { --_size: var(--avatar-xl); } /* sheet head icon ring */
|
|
2234
2266
|
/* outline avatar: no fill, an edge — icons in a ring, "add" slots */
|
|
2235
|
-
.avatar--outline { background: transparent; color: inherit; border: var(--border-w) solid var(--outline); }
|
|
2236
|
-
.avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--outline); cursor: pointer; }
|
|
2267
|
+
.avatar--outline { background: transparent; color: inherit; border: var(--border-w) solid var(--avatar-outline-border, var(--outline)); }
|
|
2268
|
+
.avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--avatar-add-border, var(--outline)); cursor: pointer; }
|
|
2237
2269
|
/* accent avatar: the "+7" tail, the selected person */
|
|
2238
|
-
.avatar--on { background: var(--accent); color: var(--on-accent); }
|
|
2270
|
+
.avatar--on { background: var(--avatar-on-bg, var(--accent)); color: var(--avatar-on-ink, var(--on-accent)); }
|
|
2239
2271
|
/* halo: faint concentric rings behind a hero avatar (decorative) */
|
|
2240
2272
|
.avatar--halo::before {
|
|
2241
2273
|
content: "";
|
|
@@ -2257,7 +2289,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2257
2289
|
<div class="avatar-pick"><button class="avatar-pick__item"><span class="avatar avatar--lg avatar--add">+</span><span class="avatar-pick__name">Add</span><span class="avatar-pick__role">member</span></button>…</div> */
|
|
2258
2290
|
.avatar-pick {
|
|
2259
2291
|
display: flex;
|
|
2260
|
-
gap: var(--sp-4);
|
|
2292
|
+
gap: var(--avatar-pick-gap, var(--sp-4));
|
|
2261
2293
|
overflow-x: auto;
|
|
2262
2294
|
scroll-snap-type: x proximity;
|
|
2263
2295
|
scrollbar-width: none; /* starts at the card's padding edge, like every row */
|
|
@@ -2266,18 +2298,18 @@ button { font: inherit; cursor: pointer; }
|
|
|
2266
2298
|
.avatar-pick__item {
|
|
2267
2299
|
scroll-snap-align: start;
|
|
2268
2300
|
flex: none;
|
|
2269
|
-
width: var(--avatar-lg);
|
|
2301
|
+
width: var(--avatar-pick-item-w, var(--avatar-lg));
|
|
2270
2302
|
display: grid;
|
|
2271
2303
|
justify-items: center;
|
|
2272
|
-
gap: var(--sp-2);
|
|
2304
|
+
gap: var(--avatar-pick-item-gap, var(--sp-2));
|
|
2273
2305
|
border: 0;
|
|
2274
2306
|
background: transparent;
|
|
2275
2307
|
color: inherit;
|
|
2276
2308
|
padding: 0;
|
|
2277
2309
|
text-align: center;
|
|
2278
2310
|
}
|
|
2279
|
-
.avatar-pick__name { font: var(--type-label); font-size: var(--fs-sm); }
|
|
2280
|
-
.avatar-pick__role { font: var(--type-caption); color: var(--card-muted); margin-top: calc(-1 * var(--sp-1)); }
|
|
2311
|
+
.avatar-pick__name { font: var(--avatar-pick-name-font, var(--type-label)); font-size: var(--avatar-pick-name-font-size, var(--fs-sm)); }
|
|
2312
|
+
.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)); }
|
|
2281
2313
|
.card--3 .avatar-pick__role { color: inherit; opacity: 0.7; }
|
|
2282
2314
|
|
|
2283
2315
|
/* ---- css/choice.css — check, toggle, toggle chip, chip grid, days, calendar, mood, pin, composer ---- */
|
|
@@ -2295,13 +2327,13 @@ button { font: inherit; cursor: pointer; }
|
|
|
2295
2327
|
<input type="checkbox" class="check" aria-label="Done"> */
|
|
2296
2328
|
.check {
|
|
2297
2329
|
appearance: none;
|
|
2298
|
-
width: var(--tap);
|
|
2299
|
-
height: var(--tap);
|
|
2330
|
+
width: var(--check-size, var(--tap));
|
|
2331
|
+
height: var(--check-size, var(--tap));
|
|
2300
2332
|
flex: none;
|
|
2301
2333
|
margin: 0;
|
|
2302
2334
|
border-radius: 50%;
|
|
2303
|
-
border: var(--border-w) solid var(--outline);
|
|
2304
|
-
background: var(--action-bg);
|
|
2335
|
+
border: var(--border-w) solid var(--check-border, var(--outline));
|
|
2336
|
+
background: var(--check-bg, var(--action-bg));
|
|
2305
2337
|
display: grid;
|
|
2306
2338
|
place-items: center;
|
|
2307
2339
|
cursor: pointer;
|
|
@@ -2318,7 +2350,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2318
2350
|
.check:checked { background: var(--accent); border-color: transparent; }
|
|
2319
2351
|
.check:checked::after { opacity: 1; }
|
|
2320
2352
|
.check:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
2321
|
-
.check--sm { width: var(--tap-sm); height: var(--tap-sm); }
|
|
2353
|
+
.check--sm { width: var(--check-sm-size, var(--tap-sm)); height: var(--check-sm-size, var(--tap-sm)); }
|
|
2322
2354
|
|
|
2323
2355
|
/* ---- toggle: big switch ----
|
|
2324
2356
|
<input type="checkbox" role="switch" class="toggle" aria-label="Private"> */
|
|
@@ -2331,9 +2363,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
2331
2363
|
height: var(--_h);
|
|
2332
2364
|
flex: none;
|
|
2333
2365
|
margin: 0;
|
|
2334
|
-
border-radius: var(--r-chip);
|
|
2335
|
-
border: var(--border-w) solid var(--outline);
|
|
2336
|
-
background: var(--action-bg);
|
|
2366
|
+
border-radius: var(--toggle-radius, var(--r-chip));
|
|
2367
|
+
border: var(--border-w) solid var(--toggle-border, var(--outline));
|
|
2368
|
+
background: var(--toggle-bg, var(--action-bg));
|
|
2337
2369
|
position: relative;
|
|
2338
2370
|
cursor: pointer;
|
|
2339
2371
|
transition: background 0.2s ease;
|
|
@@ -2362,7 +2394,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2362
2394
|
.chip-grid {
|
|
2363
2395
|
display: grid;
|
|
2364
2396
|
grid-template-columns: repeat(auto-fill, minmax(min(5.5rem, 100%), 1fr));
|
|
2365
|
-
gap: var(--sp-2);
|
|
2397
|
+
gap: var(--chip-grid-gap, var(--sp-2));
|
|
2366
2398
|
}
|
|
2367
2399
|
/* .chip hugs its text as a loose grid child (justify-self: start); inside a
|
|
2368
2400
|
chip grid the whole point is equal, full-width pick targets, so stretch.
|
|
@@ -2389,7 +2421,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2389
2421
|
|
|
2390
2422
|
/* ---- day strip: a week, one day selected ----
|
|
2391
2423
|
<div class="day-strip" role="tablist"><button class="day" role="tab" aria-selected="true"><span class="day__name">Thu</span><span class="day__num">16</span></button>…</div> */
|
|
2392
|
-
.day-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--sp-1); }
|
|
2424
|
+
.day-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--day-strip-gap, var(--sp-1)); }
|
|
2393
2425
|
.day {
|
|
2394
2426
|
display: grid;
|
|
2395
2427
|
/* three rows, always: name · number · dot. The dot's row is reserved even on
|
|
@@ -2399,33 +2431,33 @@ button { font: inherit; cursor: pointer; }
|
|
|
2399
2431
|
grid-template-rows: auto auto var(--dot-xs);
|
|
2400
2432
|
align-content: center; /* the group sits centred in the cell, not spread out */
|
|
2401
2433
|
justify-items: center;
|
|
2402
|
-
gap: var(--sp-0);
|
|
2434
|
+
gap: var(--day-gap, var(--sp-0));
|
|
2403
2435
|
min-width: 0;
|
|
2404
|
-
min-height: var(--bar-h);
|
|
2405
|
-
padding-block: var(--sp-3); /* equal air top and bottom, so the selected pill
|
|
2436
|
+
min-height: var(--day-min-h, var(--bar-h));
|
|
2437
|
+
padding-block: var(--day-pad-y, var(--sp-3)); /* equal air top and bottom, so the selected pill
|
|
2406
2438
|
is the same shape on every day */
|
|
2407
2439
|
padding-inline: 0;
|
|
2408
2440
|
border: 0;
|
|
2409
|
-
border-radius: var(--r-chip);
|
|
2441
|
+
border-radius: var(--day-radius, var(--r-chip));
|
|
2410
2442
|
background: transparent;
|
|
2411
2443
|
color: inherit;
|
|
2412
2444
|
opacity: 0.6;
|
|
2413
2445
|
}
|
|
2414
|
-
.day__name { font: var(--type-caption); }
|
|
2446
|
+
.day__name { font: var(--day-name-font, var(--type-caption)); }
|
|
2415
2447
|
.day__num { font: 700 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
|
|
2416
|
-
.day[aria-selected="true"] { background: var(--accent); color: var(--on-accent); opacity: 1; }
|
|
2417
|
-
.day__dot { grid-row: 3; align-self: center; width: var(--dot-xs); height: var(--dot-xs); border-radius: 50%; background: currentColor; }
|
|
2448
|
+
.day[aria-selected="true"] { background: var(--day-selected-bg, var(--accent)); color: var(--day-selected-ink, var(--on-accent)); opacity: 1; }
|
|
2449
|
+
.day__dot { grid-row: 3; align-self: center; width: var(--day-dot-size, var(--dot-xs)); height: var(--day-dot-size, var(--dot-xs)); border-radius: 50%; background: currentColor; }
|
|
2418
2450
|
|
|
2419
2451
|
/* ---- calendar: month grid, 44px cells, one accent day, optional marks ----
|
|
2420
2452
|
<div class="calendar"><div class="calendar__head">…</div><div class="calendar__grid"><span class="calendar__wd">Mo</span>×7 <button class="calendar__day" style="--start: 3">1</button>…</div></div> */
|
|
2421
|
-
.calendar { display: grid; gap: var(--sp-3); }
|
|
2422
|
-
.calendar__head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
|
|
2423
|
-
.calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--sp-1); justify-items: center; }
|
|
2424
|
-
.calendar__wd { font: var(--type-overline); letter-spacing: var(--track-overline); text-transform: uppercase; color: var(--card-muted); padding-block: var(--sp-2); }
|
|
2453
|
+
.calendar { display: grid; gap: var(--calendar-gap, var(--sp-3)); }
|
|
2454
|
+
.calendar__head { display: flex; align-items: center; justify-content: space-between; gap: var(--calendar-head-gap, var(--sp-2)); }
|
|
2455
|
+
.calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--calendar-grid-gap, var(--sp-1)); justify-items: center; }
|
|
2456
|
+
.calendar__wd { font: var(--calendar-wd-font, var(--type-overline)); letter-spacing: var(--track-overline); text-transform: uppercase; color: var(--calendar-wd-ink, var(--card-muted)); padding-block: var(--calendar-wd-pad-y, var(--sp-2)); }
|
|
2425
2457
|
.card--3 .calendar__wd { color: inherit; opacity: 0.7; }
|
|
2426
2458
|
.calendar__day {
|
|
2427
2459
|
width: 100%;
|
|
2428
|
-
max-width: var(--tap);
|
|
2460
|
+
max-width: var(--calendar-day-max-w, var(--tap));
|
|
2429
2461
|
aspect-ratio: 1;
|
|
2430
2462
|
display: grid;
|
|
2431
2463
|
place-items: center;
|
|
@@ -2437,9 +2469,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
2437
2469
|
font-variant-numeric: tabular-nums;
|
|
2438
2470
|
grid-column-start: var(--start, auto); /* --start on the 1st: weekday column 1–7 */
|
|
2439
2471
|
}
|
|
2440
|
-
.calendar__day--mark { background: var(--accent-soft); }
|
|
2441
|
-
.calendar__day--mark2 { background: var(--accent-mid); color: var(--on-accent); }
|
|
2442
|
-
.calendar__day[aria-selected="true"] { background: var(--accent); color: var(--on-accent); font-weight: 700; }
|
|
2472
|
+
.calendar__day--mark { background: var(--calendar-day-mark-bg, var(--accent-soft)); }
|
|
2473
|
+
.calendar__day--mark2 { background: var(--calendar-day-mark2-bg, var(--accent-mid)); color: var(--calendar-day-mark2-ink, var(--on-accent)); }
|
|
2474
|
+
.calendar__day[aria-selected="true"] { background: var(--calendar-day-selected-bg, var(--accent)); color: var(--calendar-day-selected-ink, var(--on-accent)); font-weight: 700; }
|
|
2443
2475
|
.calendar__day:disabled { opacity: 0.3; }
|
|
2444
2476
|
|
|
2445
2477
|
/* ---- mood: five dots in a pill, the picked one grows ----
|
|
@@ -2447,19 +2479,19 @@ button { font: inherit; cursor: pointer; }
|
|
|
2447
2479
|
.mood {
|
|
2448
2480
|
margin: 0;
|
|
2449
2481
|
min-inline-size: 0; /* fieldset UA default is min-content — it must shrink with the card */
|
|
2450
|
-
padding: var(--sp-1);
|
|
2451
|
-
border: var(--border-w) solid var(--outline);
|
|
2452
|
-
border-radius: var(--r-chip);
|
|
2453
|
-
background: var(--action-bg);
|
|
2454
|
-
color: var(--action-ink);
|
|
2482
|
+
padding: var(--mood-pad, var(--sp-1));
|
|
2483
|
+
border: var(--border-w) solid var(--mood-border, var(--outline));
|
|
2484
|
+
border-radius: var(--mood-radius, var(--r-chip));
|
|
2485
|
+
background: var(--mood-bg, var(--action-bg));
|
|
2486
|
+
color: var(--mood-ink, var(--action-ink));
|
|
2455
2487
|
display: flex;
|
|
2456
2488
|
justify-content: space-between;
|
|
2457
2489
|
}
|
|
2458
|
-
.mood__opt { flex: 1 1 0; max-width: var(--tap); min-width: 0; height: var(--tap); display: grid; place-items: center; position: relative; cursor: pointer; }
|
|
2490
|
+
.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; cursor: pointer; }
|
|
2459
2491
|
.mood__opt > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
2460
2492
|
.mood__dot {
|
|
2461
|
-
width: var(--icon-xs);
|
|
2462
|
-
height: var(--icon-xs);
|
|
2493
|
+
width: var(--mood-dot-size, var(--icon-xs));
|
|
2494
|
+
height: var(--mood-dot-size, var(--icon-xs));
|
|
2463
2495
|
border-radius: 50%;
|
|
2464
2496
|
background: var(--_mood, currentColor);
|
|
2465
2497
|
transition: scale 0.2s ease, box-shadow 0.2s ease;
|
|
@@ -2474,18 +2506,18 @@ button { font: inherit; cursor: pointer; }
|
|
|
2474
2506
|
|
|
2475
2507
|
/* ---- pin: big code entry, one digit per pill ----
|
|
2476
2508
|
<div class="pin"><input class="pin__cell" inputmode="numeric" maxlength="1" placeholder=" ">×4</div> */
|
|
2477
|
-
.pin { display: flex; gap: var(--sp-3); }
|
|
2509
|
+
.pin { display: flex; gap: var(--pin-gap, var(--sp-3)); }
|
|
2478
2510
|
.pin__cell {
|
|
2479
2511
|
flex: 1;
|
|
2480
2512
|
min-width: 0;
|
|
2481
|
-
max-width: var(--fab);
|
|
2513
|
+
max-width: var(--pin-cell-max-w, var(--fab));
|
|
2482
2514
|
aspect-ratio: 2 / 3;
|
|
2483
|
-
border: var(--border-w) solid var(--outline);
|
|
2484
|
-
border-radius: var(--r-chip);
|
|
2485
|
-
background: var(--action-bg);
|
|
2486
|
-
color: var(--action-ink);
|
|
2515
|
+
border: var(--border-w) solid var(--pin-cell-border, var(--outline));
|
|
2516
|
+
border-radius: var(--pin-cell-radius, var(--r-chip));
|
|
2517
|
+
background: var(--pin-cell-bg, var(--action-bg));
|
|
2518
|
+
color: var(--pin-cell-ink, var(--action-ink));
|
|
2487
2519
|
text-align: center;
|
|
2488
|
-
font: var(--type-stat);
|
|
2520
|
+
font: var(--pin-cell-font, var(--type-stat));
|
|
2489
2521
|
font-variant-numeric: tabular-nums;
|
|
2490
2522
|
caret-color: currentColor;
|
|
2491
2523
|
}
|
|
@@ -2495,7 +2527,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2495
2527
|
/* ---- composer: big text area with a bottom bar (icon · hint · send) ----
|
|
2496
2528
|
<div class="composer"><textarea class="composer__input" placeholder="What do you need?"></textarea>
|
|
2497
2529
|
<div class="composer__bar"><button class="icon-btn">…</button><span class="composer__hint">Your idea is born</span><button class="icon-btn icon-btn--invert">↑</button></div></div> */
|
|
2498
|
-
.composer { display: grid; gap: var(--sp-3); }
|
|
2530
|
+
.composer { display: grid; gap: var(--composer-gap, var(--sp-3)); }
|
|
2499
2531
|
.composer__input {
|
|
2500
2532
|
width: 100%;
|
|
2501
2533
|
min-height: 11rem;
|
|
@@ -2509,8 +2541,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2509
2541
|
outline: none;
|
|
2510
2542
|
}
|
|
2511
2543
|
.composer__input::placeholder { color: inherit; opacity: 0.4; }
|
|
2512
|
-
.composer__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
|
|
2513
|
-
.composer__hint { font: var(--type-caption); color: var(--card-muted); }
|
|
2544
|
+
.composer__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--composer-bar-gap, var(--sp-3)); }
|
|
2545
|
+
.composer__hint { font: var(--composer-hint-font, var(--type-caption)); color: var(--composer-hint-ink, var(--card-muted)); }
|
|
2514
2546
|
.card--3 .composer__hint { color: inherit; opacity: 0.7; }
|
|
2515
2547
|
|
|
2516
2548
|
/* ---- slider: one value along a track, a thumb-size knob ----
|
|
@@ -2519,7 +2551,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2519
2551
|
slider does, the input's aria-label is enough. The track is a hairline rail;
|
|
2520
2552
|
the knob is a full --tap, and the whole range is --tap tall, so the thumb has
|
|
2521
2553
|
a real target anywhere along it. The value is the input's own. */
|
|
2522
|
-
.slider { display: grid; gap: var(--sp-1); flex: 1; min-width: 0; position: relative; } /* relative: the glyph rides the knob */
|
|
2554
|
+
.slider { display: grid; gap: var(--slider-gap, var(--sp-1)); flex: 1; min-width: 0; position: relative; } /* relative: the glyph rides the knob */
|
|
2523
2555
|
/* ---- the sample glyph: one letter IN the knob, on-accent, at the type size
|
|
2524
2556
|
the slider sets — so a text-size slider shows the size before anything is
|
|
2525
2557
|
read (Lh, 2026-09-12). cardds.js publishes the knob's place on the .slider
|
|
@@ -2530,8 +2562,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2530
2562
|
position: absolute;
|
|
2531
2563
|
bottom: 0;
|
|
2532
2564
|
left: calc(var(--tap) / 2 + (100% - var(--tap)) * var(--slider-at, 0));
|
|
2533
|
-
width: var(--tap);
|
|
2534
|
-
height: var(--tap);
|
|
2565
|
+
width: var(--slider-glyph-size, var(--tap));
|
|
2566
|
+
height: var(--slider-glyph-size, var(--tap));
|
|
2535
2567
|
/* the eye centres the INK, not the line box: a letter that stands on the
|
|
2536
2568
|
baseline (ก) has no descender, while the face's box keeps 0.30em under it
|
|
2537
2569
|
(fonts.css) — so the ink lands low when the box is centred; lifted 0.15em
|
|
@@ -2539,15 +2571,15 @@ button { font: inherit; cursor: pointer; }
|
|
|
2539
2571
|
translate: -50% -0.15em;
|
|
2540
2572
|
display: grid;
|
|
2541
2573
|
place-items: center;
|
|
2542
|
-
font: var(--type-h2);
|
|
2543
|
-
color: var(--on-accent);
|
|
2574
|
+
font: var(--slider-glyph-font, var(--type-h2));
|
|
2575
|
+
color: var(--slider-glyph-ink, var(--on-accent));
|
|
2544
2576
|
pointer-events: none;
|
|
2545
2577
|
}
|
|
2546
|
-
.slider__label { font: var(--type-label); }
|
|
2578
|
+
.slider__label { font: var(--slider-label-font, var(--type-label)); }
|
|
2547
2579
|
.slider__range {
|
|
2548
2580
|
appearance: none;
|
|
2549
2581
|
width: 100%;
|
|
2550
|
-
height: var(--tap);
|
|
2582
|
+
height: var(--slider-range-h, var(--tap));
|
|
2551
2583
|
margin: 0;
|
|
2552
2584
|
background: transparent;
|
|
2553
2585
|
cursor: pointer;
|
|
@@ -2588,8 +2620,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2588
2620
|
.row {
|
|
2589
2621
|
display: flex;
|
|
2590
2622
|
align-items: center;
|
|
2591
|
-
gap: var(--sp-3);
|
|
2592
|
-
min-height: var(--tap); /* the tap target's room — the row's only vertical size */
|
|
2623
|
+
gap: var(--row-gap, var(--sp-3));
|
|
2624
|
+
min-height: var(--row-min-h, var(--tap)); /* the tap target's room — the row's only vertical size */
|
|
2593
2625
|
min-width: 0;
|
|
2594
2626
|
color: inherit;
|
|
2595
2627
|
text-decoration: none; /* .row works as <a> too — it is a tap target */
|
|
@@ -2598,21 +2630,30 @@ button { font: inherit; cursor: pointer; }
|
|
|
2598
2630
|
Space BETWEEN rows is .rows' gap; a lone row as card content takes the card's
|
|
2599
2631
|
gap. (.row--flush, which zeroed the old padding, is gone — every row is flush.) */
|
|
2600
2632
|
.row__lead {
|
|
2601
|
-
width: var(--tap);
|
|
2602
|
-
height: var(--tap);
|
|
2633
|
+
width: var(--row-lead-size, var(--tap));
|
|
2634
|
+
height: var(--row-lead-size, var(--tap));
|
|
2603
2635
|
flex: none;
|
|
2604
|
-
border-radius: var(--r-control);
|
|
2605
|
-
border: var(--border-w) solid var(--outline);
|
|
2636
|
+
border-radius: var(--row-lead-radius, var(--r-control));
|
|
2637
|
+
border: var(--border-w) solid var(--row-lead-border, var(--outline));
|
|
2606
2638
|
display: grid;
|
|
2607
2639
|
place-items: center;
|
|
2608
2640
|
}
|
|
2609
|
-
.row__body { flex: 1; min-width: 0; display: grid; gap: var(--sp-0); }
|
|
2610
|
-
.row__title { font: var(--type-title); overflow-wrap: anywhere; } /* wraps freely — no clip, so no padding trick for Thai marks (Lh 2026-09-16, same call as the card heading) */
|
|
2611
|
-
.row__meta { font: var(--type-caption); color: var(--card-muted); display: flex; align-items: center; gap: var(--sp-1); }
|
|
2641
|
+
.row__body { flex: 1; min-width: 0; display: grid; gap: var(--row-body-gap, var(--sp-0)); }
|
|
2642
|
+
.row__title { font: var(--row-title-font, var(--type-title)); overflow-wrap: anywhere; } /* wraps freely — no clip, so no padding trick for Thai marks (Lh 2026-09-16, same call as the card heading) */
|
|
2643
|
+
.row__meta { font: var(--row-meta-font, var(--type-caption)); color: var(--row-meta-ink, var(--card-muted)); display: flex; align-items: center; gap: var(--row-meta-gap, var(--sp-1)); }
|
|
2612
2644
|
.card--3 .row__meta { color: inherit; opacity: 0.7; }
|
|
2613
|
-
.row__end { margin-left: auto; text-align: right; display: grid; gap: var(--sp-0); justify-items: end; flex: none; }
|
|
2614
|
-
.row--lg { min-height: var(--fab); } /* menu rows: a taller target, round arrow at the end; .rows gives them more air */
|
|
2645
|
+
.row__end { margin-left: auto; text-align: right; display: grid; gap: var(--row-end-gap, var(--sp-0)); justify-items: end; flex: none; }
|
|
2646
|
+
.row--lg { min-height: var(--row-lg-min-h, var(--fab)); } /* menu rows: a taller target, round arrow at the end; .rows gives them more air */
|
|
2615
2647
|
.row--lg .row__title { font: 700 var(--fs-lg)/1.2 var(--font-display); }
|
|
2648
|
+
/* a row that EXPANDS (Lh 2026-09-22): the row is a <details>' summary, what it shows comes out under it —
|
|
2649
|
+
who liked a postcard, the rest of a list. An end chevron turns when open. The app owns the list.
|
|
2650
|
+
<details class="row-expand"><summary class="row">…lead · body · end…</summary><div class="row-expand__more">…</div></details> */
|
|
2651
|
+
.row-expand { display: block; }
|
|
2652
|
+
.row-expand > summary { list-style: none; cursor: pointer; }
|
|
2653
|
+
.row-expand > summary::-webkit-details-marker { display: none; }
|
|
2654
|
+
.row-expand > summary > .icon:last-child { transition: rotate var(--motion-slide); }
|
|
2655
|
+
.row-expand[open] > summary > .icon:last-child { rotate: 180deg; }
|
|
2656
|
+
.row-expand__more { padding-top: var(--row-expand-gap, var(--sp-3)); }
|
|
2616
2657
|
/* a finished item in a checklist (a to-do list, an onboarding "complete your introduction" list):
|
|
2617
2658
|
the words go muted, a filled tick sits at the end, and the row is no longer a tap target —
|
|
2618
2659
|
pointer-events off here, aria-disabled from the markup. The tick is the .check's checked look
|
|
@@ -2621,13 +2662,13 @@ button { font: inherit; cursor: pointer; }
|
|
|
2621
2662
|
.row--done .row__title { color: var(--card-muted); }
|
|
2622
2663
|
.card--3 .row--done .row__title { color: inherit; opacity: 0.7; }
|
|
2623
2664
|
.row__done {
|
|
2624
|
-
width: var(--tap-sm);
|
|
2625
|
-
height: var(--tap-sm);
|
|
2665
|
+
width: var(--row-done-size, var(--tap-sm));
|
|
2666
|
+
height: var(--row-done-size, var(--tap-sm));
|
|
2626
2667
|
flex: none;
|
|
2627
2668
|
margin-left: auto;
|
|
2628
2669
|
border-radius: 50%;
|
|
2629
|
-
background: var(--accent);
|
|
2630
|
-
color: var(--on-accent);
|
|
2670
|
+
background: var(--row-done-bg, var(--accent));
|
|
2671
|
+
color: var(--row-done-ink, var(--on-accent));
|
|
2631
2672
|
display: grid;
|
|
2632
2673
|
place-items: center;
|
|
2633
2674
|
}
|
|
@@ -2648,20 +2689,20 @@ button { font: inherit; cursor: pointer; }
|
|
|
2648
2689
|
/* ---- kv: label left, value right ----
|
|
2649
2690
|
<dl class="kv"><div class="kv__row"><dt class="kv__key">Pay with</dt><dd class="kv__val">GHO</dd></div>…</dl> */
|
|
2650
2691
|
.kv { margin: 0; display: grid; }
|
|
2651
|
-
.kv__row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); min-height: var(--tap); }
|
|
2692
|
+
.kv__row { display: flex; align-items: center; justify-content: space-between; gap: var(--kv-row-gap, var(--sp-3)); min-height: var(--kv-row-min-h, var(--tap)); }
|
|
2652
2693
|
.kv--lined > .kv__row + .kv__row { border-top: var(--border-w) solid var(--hairline); }
|
|
2653
|
-
.kv__key { font: var(--type-body); color: var(--card-muted); }
|
|
2694
|
+
.kv__key { font: var(--kv-key-font, var(--type-body)); color: var(--kv-key-ink, var(--card-muted)); }
|
|
2654
2695
|
.card--3 .kv__key { color: inherit; opacity: 0.7; }
|
|
2655
|
-
.kv__val { margin: 0; font: var(--type-title); display: inline-flex; align-items: center; gap: var(--sp-2); text-align: right; min-width: 0; }
|
|
2696
|
+
.kv__val { margin: 0; font: var(--kv-val-font, var(--type-title)); display: inline-flex; align-items: center; gap: var(--kv-val-gap, var(--sp-2)); text-align: right; min-width: 0; }
|
|
2656
2697
|
|
|
2657
2698
|
/* ---- kv grid: value over label, cells split by a rule (Sex | Weight) ----
|
|
2658
2699
|
<div class="kv-grid"><div class="kv-cell"><svg class="icon"/><span class="kv-cell__val">15.6 kg</span><span class="kv-cell__key">Weight</span></div>…</div> */
|
|
2659
|
-
.kv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(var(--tile-min), 100%), 1fr)); gap: var(--sp-4); }
|
|
2700
|
+
.kv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(var(--tile-min), 100%), 1fr)); gap: var(--kv-grid-gap, var(--sp-4)); }
|
|
2660
2701
|
.kv-grid > .kv-cell + .kv-cell { border-left: var(--border-w) solid var(--hairline); padding-left: var(--sp-4); }
|
|
2661
|
-
.kv-cell { display: grid; gap: var(--sp-0); align-content: start; min-width: 0; }
|
|
2702
|
+
.kv-cell { display: grid; gap: var(--kv-cell-gap, var(--sp-0)); align-content: start; min-width: 0; }
|
|
2662
2703
|
.kv-cell > .icon { margin-bottom: var(--sp-1); }
|
|
2663
|
-
.kv-cell__val { font: var(--type-h2); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
|
|
2664
|
-
.kv-cell__key { font: var(--type-caption); color: var(--card-muted); }
|
|
2704
|
+
.kv-cell__val { font: var(--kv-cell-val-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
|
|
2705
|
+
.kv-cell__key { font: var(--kv-cell-key-font, var(--type-caption)); color: var(--kv-cell-key-ink, var(--card-muted)); }
|
|
2665
2706
|
.card--3 .kv-cell__key { color: inherit; opacity: 0.7; }
|
|
2666
2707
|
|
|
2667
2708
|
/* ---- bubble: a chat message — theirs on the left, mine on the right (Lh 2026-09-18, replaces the chip's tail) ----
|
|
@@ -2673,7 +2714,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2673
2714
|
<div class="bubble"><p class="bubble__body">…</p><span class="bubble__actions">…icon-btns…</span><span class="bubble__meta">Dec 4 · 8:15</span></div>
|
|
2674
2715
|
<div class="bubble bubble--right">…</div>
|
|
2675
2716
|
</div> */
|
|
2676
|
-
.thread { display: grid; gap: var(--sp-4); }
|
|
2717
|
+
.thread { display: grid; gap: var(--thread-gap, var(--sp-4)); }
|
|
2677
2718
|
/* two columns: the message (max-content, but it gives way — minmax(0) — so a long one shrinks
|
|
2678
2719
|
and wraps while the actions stay beside it) and the actions; the meta on a second row under the message */
|
|
2679
2720
|
.bubble {
|
|
@@ -2693,12 +2734,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
2693
2734
|
.bubble__body {
|
|
2694
2735
|
margin: 0;
|
|
2695
2736
|
min-width: 0;
|
|
2696
|
-
padding: var(--sp-3) var(--sp-4);
|
|
2697
|
-
border-radius: var(--r-card);
|
|
2737
|
+
padding: var(--bubble-body-pad-y, var(--sp-3)) var(--bubble-body-pad-x, var(--sp-4));
|
|
2738
|
+
border-radius: var(--bubble-body-radius, var(--r-card));
|
|
2698
2739
|
border-bottom-left-radius: var(--r-control);
|
|
2699
|
-
background: var(--card-2-bg);
|
|
2700
|
-
color: var(--card-2-ink);
|
|
2701
|
-
font: var(--type-body);
|
|
2740
|
+
background: var(--bubble-body-bg, var(--card-2-bg));
|
|
2741
|
+
color: var(--bubble-body-ink, var(--card-2-ink));
|
|
2742
|
+
font: var(--bubble-body-font, var(--type-body));
|
|
2702
2743
|
overflow-wrap: anywhere;
|
|
2703
2744
|
}
|
|
2704
2745
|
.bubble--right > .bubble__body {
|
|
@@ -2708,8 +2749,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
2708
2749
|
color: var(--on-accent);
|
|
2709
2750
|
text-align: right;
|
|
2710
2751
|
}
|
|
2711
|
-
.bubble__actions { display: flex; gap: var(--sp-1); flex: none; opacity: 0.7; }
|
|
2712
|
-
.bubble__meta { display: flex; align-items: center; gap: var(--sp-1); font: var(--type-caption); color: var(--on-surface-muted); }
|
|
2752
|
+
.bubble__actions { display: flex; gap: var(--bubble-actions-gap, var(--sp-1)); flex: none; opacity: 0.7; }
|
|
2753
|
+
.bubble__meta { display: flex; align-items: center; gap: var(--bubble-meta-gap, var(--sp-1)); font: var(--bubble-meta-font, var(--type-caption)); color: var(--bubble-meta-ink, var(--on-surface-muted)); }
|
|
2713
2754
|
.bubble--right > .bubble__meta { justify-self: end; }
|
|
2714
2755
|
.bubble__meta > .icon { color: var(--accent); }
|
|
2715
2756
|
.card--3 .bubble__meta { color: inherit; opacity: 0.7; }
|
|
@@ -2721,7 +2762,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2721
2762
|
margin: 0;
|
|
2722
2763
|
padding: 0 0 0 var(--sp-6);
|
|
2723
2764
|
display: grid;
|
|
2724
|
-
gap: var(--sp-5);
|
|
2765
|
+
gap: var(--timeline-gap, var(--sp-5));
|
|
2725
2766
|
position: relative;
|
|
2726
2767
|
}
|
|
2727
2768
|
.timeline::before {
|
|
@@ -2732,7 +2773,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2732
2773
|
bottom: var(--sp-3);
|
|
2733
2774
|
border-left: var(--border-w) solid var(--hairline);
|
|
2734
2775
|
}
|
|
2735
|
-
.timeline__item { display: grid; gap: var(--sp-2); position: relative; min-width: 0; }
|
|
2776
|
+
.timeline__item { display: grid; gap: var(--timeline-item-gap, var(--sp-2)); position: relative; min-width: 0; }
|
|
2736
2777
|
.timeline__item::before {
|
|
2737
2778
|
content: "";
|
|
2738
2779
|
position: absolute;
|
|
@@ -2745,37 +2786,37 @@ button { font: inherit; cursor: pointer; }
|
|
|
2745
2786
|
}
|
|
2746
2787
|
.timeline__mark {
|
|
2747
2788
|
justify-self: start;
|
|
2748
|
-
font: var(--type-overline);
|
|
2789
|
+
font: var(--timeline-mark-font, var(--type-overline));
|
|
2749
2790
|
letter-spacing: var(--track-overline);
|
|
2750
2791
|
text-transform: uppercase;
|
|
2751
|
-
padding: var(--sp-1) var(--sp-2);
|
|
2752
|
-
border-radius: var(--r-chip);
|
|
2753
|
-
background: var(--ink-tint);
|
|
2792
|
+
padding: var(--timeline-mark-pad-y, var(--sp-1)) var(--timeline-mark-pad-x, var(--sp-2));
|
|
2793
|
+
border-radius: var(--timeline-mark-radius, var(--r-chip));
|
|
2794
|
+
background: var(--timeline-mark-bg, var(--ink-tint));
|
|
2754
2795
|
}
|
|
2755
2796
|
|
|
2756
2797
|
/* ---- legend: signal dots with captions ----
|
|
2757
2798
|
<div class="legend"><span class="legend__item"><span class="dot"></span> very high</span>…</div> */
|
|
2758
|
-
.legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); font: var(--type-caption); color: var(--card-muted); }
|
|
2799
|
+
.legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); font: var(--legend-font, var(--type-caption)); color: var(--legend-ink, var(--card-muted)); }
|
|
2759
2800
|
.card--3 .legend { color: inherit; opacity: 0.8; }
|
|
2760
|
-
.legend__item { display: inline-flex; align-items: center; gap: var(--sp-2); min-height: var(--icon); }
|
|
2801
|
+
.legend__item { display: inline-flex; align-items: center; gap: var(--legend-item-gap, var(--sp-2)); min-height: var(--legend-item-min-h, var(--icon)); }
|
|
2761
2802
|
|
|
2762
2803
|
/* ---- link: "See all ↗" in a card head, big enough to tap ----
|
|
2763
2804
|
<a class="link" href="#">See all <svg class="icon icon--xs"/></a> */
|
|
2764
2805
|
.link {
|
|
2765
2806
|
display: inline-flex;
|
|
2766
2807
|
align-items: center;
|
|
2767
|
-
gap: var(--sp-1);
|
|
2768
|
-
min-height: var(--tap-sm);
|
|
2808
|
+
gap: var(--link-gap, var(--sp-1));
|
|
2809
|
+
min-height: var(--link-min-h, var(--tap-sm));
|
|
2769
2810
|
color: inherit;
|
|
2770
2811
|
text-decoration: none;
|
|
2771
|
-
font: var(--type-label);
|
|
2772
|
-
font-size: var(--fs-body);
|
|
2812
|
+
font: var(--link-font, var(--type-label));
|
|
2813
|
+
font-size: var(--link-font-size, var(--fs-body));
|
|
2773
2814
|
border: 0;
|
|
2774
2815
|
background: transparent;
|
|
2775
2816
|
padding: 0;
|
|
2776
2817
|
cursor: pointer;
|
|
2777
2818
|
}
|
|
2778
|
-
.link--muted { color: var(--card-muted); }
|
|
2819
|
+
.link--muted { color: var(--link-muted-ink, var(--card-muted)); }
|
|
2779
2820
|
.card--3 .link--muted { color: inherit; opacity: 0.7; }
|
|
2780
2821
|
|
|
2781
2822
|
/* ---- css/media.css — cover card, quote, tile grid, fold, mosaic, wave ---- */
|
|
@@ -2824,16 +2865,16 @@ button { font: inherit; cursor: pointer; }
|
|
|
2824
2865
|
|
|
2825
2866
|
/* ---- quote: giant mark, big text, attribution ----
|
|
2826
2867
|
<blockquote class="quote"><p class="quote__text">It's overcast…</p><footer class="quote__by">Wednesday 15</footer></blockquote> */
|
|
2827
|
-
.quote { margin: 0; display: grid; gap: var(--sp-3); }
|
|
2868
|
+
.quote { margin: 0; display: grid; gap: var(--quote-gap, var(--sp-3)); }
|
|
2828
2869
|
.quote::before {
|
|
2829
2870
|
content: "\201C";
|
|
2830
2871
|
font: 900 calc(var(--fs-h1) * 3)/0.55 var(--font-display);
|
|
2831
2872
|
height: var(--sp-10);
|
|
2832
2873
|
opacity: 0.9;
|
|
2833
2874
|
}
|
|
2834
|
-
.quote__text { margin: 0; font: var(--type-h1); letter-spacing: var(--track-h1); text-wrap: balance; }
|
|
2875
|
+
.quote__text { margin: 0; font: var(--quote-text-font, var(--type-h1)); letter-spacing: var(--track-h1); text-wrap: balance; }
|
|
2835
2876
|
.quote--sm .quote__text { font: var(--type-h2); letter-spacing: var(--track-h2); }
|
|
2836
|
-
.quote__by { font: var(--type-overline); letter-spacing: var(--track-overline); text-transform: uppercase; color: var(--card-muted); }
|
|
2877
|
+
.quote__by { font: var(--quote-by-font, var(--type-overline)); letter-spacing: var(--track-overline); text-transform: uppercase; color: var(--quote-by-ink, var(--card-muted)); }
|
|
2837
2878
|
.card--3 .quote__by { color: inherit; opacity: 0.7; }
|
|
2838
2879
|
|
|
2839
2880
|
/* ---- tile grid: big square tiles — icon top, label bottom ----
|
|
@@ -2842,36 +2883,36 @@ button { font: inherit; cursor: pointer; }
|
|
|
2842
2883
|
.tile-grid {
|
|
2843
2884
|
display: grid;
|
|
2844
2885
|
grid-template-columns: repeat(auto-fill, minmax(min(var(--tile-min), 100%), 1fr));
|
|
2845
|
-
gap: var(--sp-3);
|
|
2886
|
+
gap: var(--tile-grid-gap, var(--sp-3));
|
|
2846
2887
|
}
|
|
2847
2888
|
.tile-grid--3 { --tile-min: 6rem; } /* three across on a phone (attachments, upload kinds) */
|
|
2848
2889
|
.tile {
|
|
2849
2890
|
display: grid;
|
|
2850
2891
|
align-content: space-between;
|
|
2851
2892
|
justify-items: start;
|
|
2852
|
-
gap: var(--sp-5);
|
|
2853
|
-
min-height: var(--tile-h);
|
|
2893
|
+
gap: var(--tile-gap, var(--sp-5));
|
|
2894
|
+
min-height: var(--tile-min-h, var(--tile-h));
|
|
2854
2895
|
min-width: 0;
|
|
2855
|
-
padding: var(--sp-4);
|
|
2856
|
-
border: var(--border-w) solid var(--outline);
|
|
2857
|
-
border-radius: var(--r-card);
|
|
2858
|
-
background: var(--action-bg);
|
|
2859
|
-
color: var(--action-ink);
|
|
2896
|
+
padding: var(--tile-pad, var(--sp-4));
|
|
2897
|
+
border: var(--border-w) solid var(--tile-border, var(--outline));
|
|
2898
|
+
border-radius: var(--tile-radius, var(--r-card));
|
|
2899
|
+
background: var(--tile-bg, var(--action-bg));
|
|
2900
|
+
color: var(--tile-ink, var(--action-ink));
|
|
2860
2901
|
font: inherit;
|
|
2861
2902
|
text-align: left;
|
|
2862
2903
|
position: relative;
|
|
2863
2904
|
cursor: pointer;
|
|
2864
2905
|
}
|
|
2865
|
-
.tile__label { font: var(--type-title); }
|
|
2866
|
-
.tile__value { font: var(--type-h2); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
|
|
2867
|
-
.tile__sub { font: var(--type-caption); opacity: 0.7; }
|
|
2868
|
-
.tile__text { display: grid; gap: var(--sp-0); }
|
|
2869
|
-
.tile--on { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
2906
|
+
.tile__label { font: var(--tile-label-font, var(--type-title)); }
|
|
2907
|
+
.tile__value { font: var(--tile-value-font, var(--type-h2)); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
|
|
2908
|
+
.tile__sub { font: var(--tile-sub-font, var(--type-caption)); opacity: 0.7; }
|
|
2909
|
+
.tile__text { display: grid; gap: var(--tile-text-gap, var(--sp-0)); }
|
|
2910
|
+
.tile--on { background: var(--tile-on-bg, var(--accent)); color: var(--tile-on-ink, var(--on-accent)); border-color: transparent; }
|
|
2870
2911
|
.tile--pick > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
2871
2912
|
.tile--pick:has(:checked) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
2872
2913
|
.tile--pick:has(:focus-visible) { outline: var(--focus); outline-offset: var(--sp-1); }
|
|
2873
2914
|
.tile--wide { grid-column: 1 / -1; }
|
|
2874
|
-
.tile--stat { min-height: var(--avatar-xl); gap: var(--sp-3); grid-template-columns: auto 1fr; align-items: center; }
|
|
2915
|
+
.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; }
|
|
2875
2916
|
/* upload: dashed, centred plus + label */
|
|
2876
2917
|
.tile--upload {
|
|
2877
2918
|
border-style: dashed;
|
|
@@ -2879,12 +2920,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
2879
2920
|
color: inherit;
|
|
2880
2921
|
align-content: center;
|
|
2881
2922
|
justify-items: center;
|
|
2882
|
-
gap: var(--sp-2);
|
|
2923
|
+
gap: var(--tile-upload-gap, var(--sp-2));
|
|
2883
2924
|
text-align: center;
|
|
2884
2925
|
}
|
|
2885
2926
|
/* tinted tiles: slot colors inside a card (schedule, notes) */
|
|
2886
|
-
.tile--2 { background: var(--card-2-bg); color: var(--card-2-ink); border-color: transparent; }
|
|
2887
|
-
.tile--3 { background: var(--card-3-bg); color: var(--card-3-ink); border-color: transparent; }
|
|
2927
|
+
.tile--2 { background: var(--tile-2-bg, var(--card-2-bg)); color: var(--tile-2-ink, var(--card-2-ink)); border-color: transparent; }
|
|
2928
|
+
.tile--3 { background: var(--tile-3-bg, var(--card-3-bg)); color: var(--tile-3-ink, var(--card-3-ink)); border-color: transparent; }
|
|
2888
2929
|
|
|
2889
2930
|
/* ---- fold: a sticky-note corner on any card (decorative) ----
|
|
2890
2931
|
No border. The card's outline follows its border box, so it ran straight
|
|
@@ -2908,7 +2949,7 @@ button { font: inherit; cursor: pointer; }
|
|
|
2908
2949
|
<span class="wave"></span> — sits in a .row__body, fills the width */
|
|
2909
2950
|
.wave {
|
|
2910
2951
|
display: block;
|
|
2911
|
-
height: var(--icon);
|
|
2952
|
+
height: var(--wave-h, var(--icon));
|
|
2912
2953
|
width: 100%;
|
|
2913
2954
|
background: repeating-linear-gradient(90deg, currentColor 0 var(--sp-1), transparent var(--sp-1) var(--sp-2));
|
|
2914
2955
|
/* masks only read alpha: currentColor = "opaque" without naming a color */
|
|
@@ -2923,9 +2964,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
2923
2964
|
display: grid;
|
|
2924
2965
|
grid-template-columns: 2fr 1fr;
|
|
2925
2966
|
grid-auto-rows: 1fr;
|
|
2926
|
-
gap: var(--sp-1);
|
|
2967
|
+
gap: var(--mosaic-gap, var(--sp-1));
|
|
2927
2968
|
aspect-ratio: 4 / 3;
|
|
2928
|
-
border-radius: var(--r-media);
|
|
2969
|
+
border-radius: var(--mosaic-radius, var(--r-media));
|
|
2929
2970
|
overflow: hidden;
|
|
2930
2971
|
}
|
|
2931
2972
|
.mosaic > :first-child { grid-row: span 2; }
|
|
@@ -2933,59 +2974,75 @@ button { font: inherit; cursor: pointer; }
|
|
|
2933
2974
|
.mosaic > .ph { opacity: 0.4; border-width: var(--border-w); }
|
|
2934
2975
|
.mosaic > img { width: 100%; height: 100%; object-fit: cover; }
|
|
2935
2976
|
|
|
2936
|
-
/* ---- postcard: a member's introduction
|
|
2937
|
-
|
|
2977
|
+
/* ---- postcard: a member's introduction in the shape of a cover card — the photo fills it,
|
|
2978
|
+
the band at the bottom names it; tap the photo to turn it over (Lh 2026-09-12; reshaped 2026-09-22) ----
|
|
2938
2979
|
<div class="postcard">
|
|
2939
|
-
<
|
|
2940
|
-
<img src="…" alt=""> — or <div class="ph"></div>
|
|
2941
|
-
<
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2980
|
+
<div class="postcard__face postcard__front">
|
|
2981
|
+
<button class="postcard__turn" type="button" aria-label="พลิกดูด้านหลัง"><img src="…" alt=""> — or <div class="ph"></div> —</button>
|
|
2982
|
+
<div class="postcard__band">
|
|
2983
|
+
<div class="postcard__words"><span class="t-h1">ป้าแจ๋ว</span><span class="t-body">บ้านสวนผัก</span></div>
|
|
2984
|
+
<button class="icon-btn icon-btn--invert" aria-label="ถูกใจ" aria-pressed="false">♥</button>
|
|
2985
|
+
</div>
|
|
2986
|
+
</div>
|
|
2987
|
+
<div class="postcard__face postcard__back">
|
|
2988
|
+
<button class="postcard__turn" type="button" aria-label="พลิกดูด้านหน้า">
|
|
2989
|
+
<div class="postcard__words">…</div><p class="postcard__caption">ชอบชงกาแฟดริปตอนเช้า</p>
|
|
2990
|
+
</button>
|
|
2991
|
+
<div class="postcard__voice"><div class="progress"></div><span class="t-caption">0:07 / 0:12</span><button class="icon-btn icon-btn--invert" aria-label="ฟัง">▶</button></div> (a voice note: no caption — the line, its time, the control under them, at the middle)
|
|
2992
|
+
</div>
|
|
2947
2993
|
</div>
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2994
|
+
The two faces share one box and turn (a Y rotation at --motion-flip, a plain
|
|
2995
|
+
swap under prefers-reduced-motion). A face is a div: the TURN is the button
|
|
2996
|
+
that fills it (the photo, or the caption), the band lies over its foot and
|
|
2997
|
+
turns with it — words and the heart; the back sets the name a fifth of the way down, the words after it; a
|
|
2998
|
+
button in the band is never inside the turn button. The size is the cover
|
|
2999
|
+
card's (--postcard-min-h = the cover's min-height, the card radius). Who
|
|
3000
|
+
liked it, a voice note, a reply live OUTSIDE the postcard, in a Row under it. */
|
|
2954
3001
|
.postcard {
|
|
2955
3002
|
position: relative;
|
|
2956
|
-
|
|
3003
|
+
min-height: var(--postcard-min-h, 22.5rem);
|
|
2957
3004
|
perspective: 60rem;
|
|
2958
|
-
border-radius: var(--r-
|
|
3005
|
+
border-radius: var(--postcard-radius, var(--r-card));
|
|
3006
|
+
isolation: isolate;
|
|
2959
3007
|
}
|
|
2960
3008
|
.postcard__face {
|
|
2961
3009
|
position: absolute;
|
|
2962
3010
|
inset: 0;
|
|
2963
3011
|
display: grid;
|
|
2964
3012
|
align-content: end;
|
|
2965
|
-
|
|
2966
|
-
gap: var(--sp-2);
|
|
2967
|
-
border: var(--border-w) solid var(--card-border);
|
|
3013
|
+
border: var(--border-w) solid var(--postcard-face-border, var(--card-border));
|
|
2968
3014
|
border-radius: inherit;
|
|
2969
3015
|
overflow: hidden;
|
|
2970
3016
|
isolation: isolate;
|
|
2971
|
-
color: inherit;
|
|
2972
|
-
text-align: left;
|
|
2973
3017
|
backface-visibility: hidden;
|
|
2974
3018
|
-webkit-backface-visibility: hidden;
|
|
2975
3019
|
transition: transform var(--motion-flip) ease-in-out;
|
|
2976
|
-
box-shadow: var(--shadow-float);
|
|
3020
|
+
box-shadow: var(--postcard-face-shadow, var(--shadow-float));
|
|
2977
3021
|
}
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
3022
|
+
/* the turn: the button that fills the face, under the band */
|
|
3023
|
+
.postcard__turn {
|
|
3024
|
+
position: absolute;
|
|
3025
|
+
inset: 0;
|
|
3026
|
+
display: grid;
|
|
3027
|
+
align-content: start;
|
|
3028
|
+
padding: var(--postcard-face-pad, var(--card-pad-rest));
|
|
3029
|
+
border: 0;
|
|
3030
|
+
border-radius: inherit;
|
|
3031
|
+
background: transparent;
|
|
3032
|
+
color: inherit;
|
|
3033
|
+
text-align: left;
|
|
3034
|
+
cursor: pointer;
|
|
3035
|
+
}
|
|
3036
|
+
.postcard__turn:focus-visible { outline: var(--focus); outline-offset: calc(-1 * var(--sp-1)); }
|
|
3037
|
+
/* the front: the photo (or the wireframe box) fills it; the band reads on a scrim */
|
|
3038
|
+
.postcard__front { background: var(--postcard-front-bg, var(--card-3-bg)); color: var(--postcard-front-ink, var(--card-3-ink)); }
|
|
3039
|
+
.postcard__front .postcard__turn > img,
|
|
3040
|
+
.postcard__front .postcard__turn > .ph {
|
|
2983
3041
|
position: absolute;
|
|
2984
3042
|
inset: 0;
|
|
2985
3043
|
width: 100%;
|
|
2986
3044
|
height: 100%;
|
|
2987
3045
|
object-fit: cover;
|
|
2988
|
-
z-index: -2;
|
|
2989
3046
|
border: 0;
|
|
2990
3047
|
border-radius: 0;
|
|
2991
3048
|
min-height: 0;
|
|
@@ -2994,20 +3051,40 @@ button { font: inherit; cursor: pointer; }
|
|
|
2994
3051
|
content: "";
|
|
2995
3052
|
position: absolute;
|
|
2996
3053
|
inset: 0;
|
|
2997
|
-
z-index:
|
|
3054
|
+
z-index: 1;
|
|
3055
|
+
pointer-events: none;
|
|
2998
3056
|
background: linear-gradient(to top, var(--scrim), transparent 45%);
|
|
2999
3057
|
}
|
|
3000
|
-
/* the back: paper — the
|
|
3058
|
+
/* the back: paper — the caption and the name packed at the middle, big enough to read at arm's length */
|
|
3001
3059
|
.postcard__back {
|
|
3002
|
-
background: var(--card-2-bg);
|
|
3003
|
-
color: var(--card-2-ink);
|
|
3004
|
-
align-content: space-between;
|
|
3060
|
+
background: var(--postcard-back-bg, var(--card-2-bg));
|
|
3061
|
+
color: var(--postcard-back-ink, var(--card-2-ink));
|
|
3005
3062
|
transform: rotateY(180deg);
|
|
3006
3063
|
}
|
|
3007
|
-
.
|
|
3008
|
-
.
|
|
3064
|
+
.postcard__back .postcard__turn { align-content: start; padding-top: var(--postcard-back-top, 20%); gap: var(--postcard-back-gap, var(--sp-5)); } /* the name a fifth down the paper, the words after it */
|
|
3065
|
+
.postcard__caption { margin: 0; font: var(--postcard-caption-font, var(--type-title)); text-wrap: pretty; }
|
|
3066
|
+
/* the band: the face's foot — name and line left, the heart right; in flow at the face's end, over the turn.
|
|
3067
|
+
Taps between its words fall through to the turn; its buttons take their own. */
|
|
3068
|
+
.postcard__band {
|
|
3069
|
+
position: relative;
|
|
3070
|
+
z-index: 2;
|
|
3071
|
+
display: flex;
|
|
3072
|
+
align-items: end;
|
|
3073
|
+
gap: var(--postcard-band-gap, var(--sp-3));
|
|
3074
|
+
padding: var(--postcard-face-pad, var(--card-pad-rest));
|
|
3075
|
+
pointer-events: none;
|
|
3076
|
+
}
|
|
3077
|
+
.postcard__band > * { pointer-events: auto; }
|
|
3078
|
+
.postcard__words { display: grid; gap: var(--postcard-words-gap, var(--sp-1)); flex: 1; min-width: 0; pointer-events: none; }
|
|
3079
|
+
.postcard__front .postcard__band { --accent: var(--postcard-front-ink, var(--card-3-ink)); --on-accent: var(--postcard-front-bg, var(--card-3-bg)); }
|
|
3080
|
+
.postcard__band .icon-btn[aria-pressed="true"] .icon { fill: currentColor; }
|
|
3009
3081
|
.postcard.is-flipped > .postcard__front { transform: rotateY(-180deg); }
|
|
3010
3082
|
.postcard.is-flipped > .postcard__back { transform: rotateY(0); }
|
|
3083
|
+
/* a voice note on the back: no words to read — its timeline (a Progress), its length and the control to hear it
|
|
3084
|
+
under them, at the middle over the turn, on the face's inset; the app plays and keeps the time */
|
|
3085
|
+
.postcard__voice { position: absolute; inset: 0; z-index: 2; display: grid; align-content: center; justify-items: center; gap: var(--postcard-voice-gap, var(--sp-3)); padding: var(--postcard-face-pad, var(--card-pad-rest)); pointer-events: none; }
|
|
3086
|
+
.postcard__voice > * { pointer-events: auto; }
|
|
3087
|
+
.postcard__voice > .progress { justify-self: stretch; pointer-events: none; }
|
|
3011
3088
|
@media (prefers-reduced-motion: reduce) {
|
|
3012
3089
|
.postcard__face { transition: none; }
|
|
3013
3090
|
}
|
|
@@ -3020,8 +3097,8 @@ button { font: inherit; cursor: pointer; }
|
|
|
3020
3097
|
lives in sheet.css; these compose with .sheet-stage.
|
|
3021
3098
|
============================================================ */
|
|
3022
3099
|
|
|
3023
|
-
/* ----
|
|
3024
|
-
<div class="sheet-stage sheet-stage--
|
|
3100
|
+
/* ---- the Modal — the case drops in, its drawer opens below (Lh 2026-09-16: ONE card, two parts)
|
|
3101
|
+
<div class="sheet-stage sheet-stage--dim"> (dim = the page under a scrim; a Modal on a clean ground is the stage without it — Lh 2026-09-22, `--ask` retired: the stage does the ground, the modal centres itself)
|
|
3025
3102
|
<article class="card modal">
|
|
3026
3103
|
<div class="modal__lift">…the card's content (a head, rows, a timeline)…</div>
|
|
3027
3104
|
<div class="modal__drawer"><h2 class="t-h2">Take the case?</h2><p class="t-caption">This cannot be undone.</p>
|
|
@@ -3032,36 +3109,33 @@ button { font: inherit; cursor: pointer; }
|
|
|
3032
3109
|
.modal is the card — no border (the shadow is its edge on the scrim);
|
|
3033
3110
|
.modal__lift the light part that holds the thing being asked about (it
|
|
3034
3111
|
scrolls when tall); .modal__drawer the dark end that asks. The ground is
|
|
3035
|
-
the
|
|
3112
|
+
whatever the stage shows (dim it or not). Two beats (2 × --motion-sheet) when
|
|
3036
3113
|
the stage appears, and the same two backwards when it closes:
|
|
3037
3114
|
1. the card drops in from above the stage to the screen's centre — its
|
|
3038
3115
|
drawer still inside it, height 0: nothing of the ask shows yet
|
|
3039
3116
|
2. the drawer comes out of the card's bottom edge while the lift moves
|
|
3040
3117
|
up, both at once: the card grows from its centre, which never moves
|
|
3041
|
-
(the
|
|
3118
|
+
(the modal centres itself — absolute, margin auto; a growing card keeps its middle where it is)
|
|
3042
3119
|
Closing: a click on any button in the drawer plays the same animations in
|
|
3043
3120
|
reverse (cardds.js: Animation.reverse() on each — nothing duplicated), then
|
|
3044
3121
|
fires "cardds:modal" on the .sheet-stage (bubbles, detail.answer from the
|
|
3045
3122
|
button's data-answer) for the app to remove the stage.
|
|
3046
3123
|
Reduced motion: the finished state, no beats; closing fires at once. */
|
|
3047
|
-
.sheet-stage--ask {
|
|
3048
|
-
--drawer-h: 14rem;
|
|
3049
|
-
display: grid;
|
|
3050
|
-
align-content: center; /* the card at the screen's centre — and kept there as it grows */
|
|
3051
|
-
padding-inline: var(--screen-pad);
|
|
3052
|
-
}
|
|
3053
|
-
.sheet-stage--ask::after { content: ""; position: absolute; inset: 0; background: var(--scrim); } /* the page dimmed */
|
|
3054
|
-
.sheet-stage--ask > .sheet-stage__bg { filter: blur(var(--sp-2)); }
|
|
3055
3124
|
.modal {
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3125
|
+
--drawer-h: 14rem;
|
|
3126
|
+
position: absolute; /* at the stage's centre, and kept there as it grows: absolute + margin auto re-centres on every height */
|
|
3127
|
+
inset: 0;
|
|
3128
|
+
margin: auto;
|
|
3129
|
+
height: fit-content; /* inset 0 alone would stretch it to the stage: its own height, then the margins share the rest */
|
|
3130
|
+
width: calc(100% - 2 * var(--screen-pad));
|
|
3131
|
+
z-index: 2; /* over the dim stage's scrim (z 1) */
|
|
3132
|
+
max-height: calc(100cqh - 2 * var(--sp-5)); /* cqh: the stage (a size container) */
|
|
3059
3133
|
padding: 0; /* the parts pad themselves */
|
|
3060
3134
|
border: 0; /* no frame on the modal (Lh 2026-09-16) — its edge is the shadow */
|
|
3061
3135
|
display: grid;
|
|
3062
3136
|
grid-template-rows: minmax(0, 1fr) auto;
|
|
3063
3137
|
overflow: clip;
|
|
3064
|
-
box-shadow: var(--shadow-sheet);
|
|
3138
|
+
box-shadow: var(--modal-shadow, var(--shadow-sheet));
|
|
3065
3139
|
}
|
|
3066
3140
|
.modal__lift {
|
|
3067
3141
|
padding: var(--card-pad);
|
|
@@ -3075,15 +3149,15 @@ button { font: inherit; cursor: pointer; }
|
|
|
3075
3149
|
.modal__drawer {
|
|
3076
3150
|
height: var(--drawer-h);
|
|
3077
3151
|
box-sizing: border-box;
|
|
3078
|
-
padding: var(--sp-5);
|
|
3152
|
+
padding: var(--modal-drawer-pad, var(--sp-5));
|
|
3079
3153
|
overflow: clip;
|
|
3080
3154
|
display: grid;
|
|
3081
3155
|
align-content: center;
|
|
3082
3156
|
justify-items: center;
|
|
3083
|
-
gap: var(--sp-1);
|
|
3157
|
+
gap: var(--modal-drawer-gap, var(--sp-1));
|
|
3084
3158
|
text-align: center;
|
|
3085
|
-
background: var(--card-3-bg);
|
|
3086
|
-
color: var(--card-3-ink);
|
|
3159
|
+
background: var(--modal-drawer-bg, var(--card-3-bg));
|
|
3160
|
+
color: var(--modal-drawer-ink, var(--card-3-ink));
|
|
3087
3161
|
--accent: var(--card-3-ink); /* the one solid button inverts on the dark drawer */
|
|
3088
3162
|
--on-accent: var(--card-3-bg);
|
|
3089
3163
|
}
|
|
@@ -3099,14 +3173,14 @@ button { font: inherit; cursor: pointer; }
|
|
|
3099
3173
|
}
|
|
3100
3174
|
@keyframes modal-open {
|
|
3101
3175
|
0%, 50% { height: 0; padding-block: 0; } /* inside the card */
|
|
3102
|
-
100% { height: var(--drawer-h); padding-block: var(--sp-5); } /* 2. out below, the lift up, together */
|
|
3176
|
+
100% { height: var(--drawer-h); padding-block: var(--modal-drawer-pad, var(--sp-5)); } /* 2. out below, the lift up, together — the same knob the rule reads: a keyframe that pinned the value would make the knob dead */
|
|
3103
3177
|
}
|
|
3104
3178
|
}
|
|
3105
3179
|
|
|
3106
|
-
.btn-trio { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-3); }
|
|
3180
|
+
.btn-trio { display: flex; align-items: center; gap: var(--btn-trio-gap, var(--sp-3)); margin-top: var(--sp-3); }
|
|
3107
3181
|
.btn-trio__dots {
|
|
3108
|
-
width: var(--sp-5);
|
|
3109
|
-
height: var(--sp-1);
|
|
3182
|
+
width: var(--btn-trio-dots-w, var(--sp-5));
|
|
3183
|
+
height: var(--btn-trio-dots-h, var(--sp-1));
|
|
3110
3184
|
background: radial-gradient(circle, currentColor var(--sp-0), transparent calc(var(--sp-0) + 0.5px)) 0 0 / calc(var(--sp-5) / 2) 100% repeat-x;
|
|
3111
3185
|
opacity: 0.4;
|
|
3112
3186
|
}
|
|
@@ -3127,31 +3201,31 @@ button { font: inherit; cursor: pointer; }
|
|
|
3127
3201
|
display: none; /* explicit closed state — never display on the base rule */
|
|
3128
3202
|
margin: 0;
|
|
3129
3203
|
inset: auto;
|
|
3130
|
-
border: var(--border-w) solid var(--action-border);
|
|
3131
|
-
border-radius: var(--r-card);
|
|
3204
|
+
border: var(--border-w) solid var(--menu-border, var(--action-border));
|
|
3205
|
+
border-radius: var(--menu-radius, var(--r-card));
|
|
3132
3206
|
--menu-pad: var(--sp-2); /* published: a row's radius is concentric with the panel's */
|
|
3133
3207
|
padding: var(--menu-pad);
|
|
3134
3208
|
min-width: 12rem;
|
|
3135
|
-
background: var(--action-bg);
|
|
3136
|
-
color: var(--action-ink);
|
|
3137
|
-
box-shadow: var(--shadow-float);
|
|
3209
|
+
background: var(--menu-bg, var(--action-bg));
|
|
3210
|
+
color: var(--menu-ink, var(--action-ink));
|
|
3211
|
+
box-shadow: var(--menu-shadow, var(--shadow-float));
|
|
3138
3212
|
}
|
|
3139
3213
|
.menu:popover-open { display: grid; }
|
|
3140
3214
|
.menu:not([popover]) { display: grid; position: relative; } /* inline usage (no attribute) */
|
|
3141
3215
|
.menu__item {
|
|
3142
3216
|
display: flex;
|
|
3143
3217
|
align-items: center;
|
|
3144
|
-
gap: var(--sp-3);
|
|
3145
|
-
min-height: var(--tap);
|
|
3218
|
+
gap: var(--menu-item-gap, var(--sp-3));
|
|
3219
|
+
min-height: var(--menu-item-min-h, var(--tap));
|
|
3146
3220
|
width: 100%;
|
|
3147
|
-
padding: 0 var(--sp-3);
|
|
3221
|
+
padding: var(--menu-item-pad-y, 0) var(--menu-item-pad-x, var(--sp-3));
|
|
3148
3222
|
border: 0;
|
|
3149
3223
|
/* concentric with the panel: the card radius minus the panel's padding, so the
|
|
3150
3224
|
highlight's curve follows the outer corner (Lh 2026-09-16) */
|
|
3151
3225
|
border-radius: calc(var(--r-card) - var(--menu-pad, var(--sp-2)));
|
|
3152
3226
|
background: transparent;
|
|
3153
3227
|
color: inherit;
|
|
3154
|
-
font: var(--type-title);
|
|
3228
|
+
font: var(--menu-item-font, var(--type-title));
|
|
3155
3229
|
text-align: left;
|
|
3156
3230
|
cursor: pointer;
|
|
3157
3231
|
}
|
|
@@ -3179,12 +3253,12 @@ button { font: inherit; cursor: pointer; }
|
|
|
3179
3253
|
/* ---- banner: a chip that opens into a tinted card, no JS ----
|
|
3180
3254
|
<details class="banner"><summary class="chip chip--on">Save $30 on orders over $70 <svg class="icon icon--xs"/></summary>
|
|
3181
3255
|
<div class="card card--3 banner__body">…</div></details> */
|
|
3182
|
-
.banner { display: grid; gap: var(--sp-2); justify-items: start; }
|
|
3256
|
+
.banner { display: grid; gap: var(--banner-gap, var(--sp-2)); justify-items: start; }
|
|
3183
3257
|
.banner > summary { list-style: none; cursor: pointer; }
|
|
3184
3258
|
.banner > summary::-webkit-details-marker { display: none; }
|
|
3185
3259
|
.banner > summary .icon { transition: rotate 0.2s ease; }
|
|
3186
3260
|
.banner[open] > summary .icon { rotate: 180deg; }
|
|
3187
|
-
.banner__body { width: 100%; padding: var(--sp-4) var(--sp-5); --card-gap: var(--sp-1); }
|
|
3261
|
+
.banner__body { width: 100%; padding: var(--banner-body-pad-y, var(--sp-4)) var(--banner-body-pad-x, var(--sp-5)); --card-gap: var(--sp-1); }
|
|
3188
3262
|
.banner::details-content { width: 100%; } /* Chromium wraps the content in this pseudo; justify-items: start shrink-wrapped it, so the body never reached the width */
|
|
3189
3263
|
@supports (interpolate-size: allow-keywords) {
|
|
3190
3264
|
@media (prefers-reduced-motion: no-preference) {
|
|
@@ -3202,9 +3276,9 @@ button { font: inherit; cursor: pointer; }
|
|
|
3202
3276
|
bottom: var(--sp-5);
|
|
3203
3277
|
max-width: var(--screen-w);
|
|
3204
3278
|
margin-inline: auto;
|
|
3205
|
-
padding-inline: var(--sp-4);
|
|
3279
|
+
padding-inline: var(--float-bar-pad-x, var(--sp-4));
|
|
3206
3280
|
display: flex;
|
|
3207
|
-
gap: var(--sp-2);
|
|
3281
|
+
gap: var(--float-bar-gap, var(--sp-2));
|
|
3208
3282
|
align-items: center;
|
|
3209
3283
|
z-index: 30;
|
|
3210
3284
|
}
|
|
@@ -3219,29 +3293,29 @@ button { font: inherit; cursor: pointer; }
|
|
|
3219
3293
|
display: flex;
|
|
3220
3294
|
justify-content: space-around;
|
|
3221
3295
|
align-items: center;
|
|
3222
|
-
min-height: var(--bar-h);
|
|
3223
|
-
padding: var(--sp-1);
|
|
3224
|
-
border-radius: var(--r-chip);
|
|
3225
|
-
border: var(--border-w) solid var(--action-border);
|
|
3226
|
-
background: color-mix(in srgb, var(--action-bg) 82%, transparent);
|
|
3227
|
-
color: var(--action-ink);
|
|
3296
|
+
min-height: var(--float-bar-pill-min-h, var(--bar-h));
|
|
3297
|
+
padding: var(--float-bar-pill-pad, var(--sp-1));
|
|
3298
|
+
border-radius: var(--float-bar-pill-radius, var(--r-chip));
|
|
3299
|
+
border: var(--border-w) solid var(--float-bar-pill-border, var(--action-border));
|
|
3300
|
+
background: var(--float-bar-pill-bg, color-mix(in srgb, var(--action-bg) 82%, transparent));
|
|
3301
|
+
color: var(--float-bar-pill-ink, var(--action-ink));
|
|
3228
3302
|
backdrop-filter: blur(var(--sp-3));
|
|
3229
3303
|
-webkit-backdrop-filter: blur(var(--sp-3));
|
|
3230
|
-
box-shadow: var(--shadow-float);
|
|
3304
|
+
box-shadow: var(--float-bar-pill-shadow, var(--shadow-float));
|
|
3231
3305
|
}
|
|
3232
3306
|
.float-bar__item {
|
|
3233
3307
|
flex: 1;
|
|
3234
3308
|
min-width: 0;
|
|
3235
3309
|
display: grid;
|
|
3236
3310
|
justify-items: center;
|
|
3237
|
-
gap: var(--sp-0);
|
|
3238
|
-
min-height: var(--tap-lg);
|
|
3239
|
-
padding: var(--sp-1);
|
|
3311
|
+
gap: var(--float-bar-item-gap, var(--sp-0));
|
|
3312
|
+
min-height: var(--float-bar-item-min-h, var(--tap-lg));
|
|
3313
|
+
padding: var(--float-bar-item-pad, var(--sp-1));
|
|
3240
3314
|
border: 0;
|
|
3241
|
-
border-radius: var(--r-chip);
|
|
3315
|
+
border-radius: var(--float-bar-item-radius, var(--r-chip));
|
|
3242
3316
|
background: transparent;
|
|
3243
3317
|
color: inherit;
|
|
3244
|
-
font: var(--type-caption);
|
|
3318
|
+
font: var(--float-bar-item-font, var(--type-caption));
|
|
3245
3319
|
text-decoration: none;
|
|
3246
3320
|
opacity: 0.6;
|
|
3247
3321
|
overflow: hidden;
|
|
@@ -3259,15 +3333,15 @@ button { font: inherit; cursor: pointer; }
|
|
|
3259
3333
|
.callout {
|
|
3260
3334
|
position: absolute;
|
|
3261
3335
|
display: grid;
|
|
3262
|
-
gap: var(--sp-0);
|
|
3336
|
+
gap: var(--callout-gap, var(--sp-0));
|
|
3263
3337
|
width: max-content;
|
|
3264
3338
|
max-width: 15rem;
|
|
3265
|
-
padding: var(--sp-3) var(--sp-4);
|
|
3266
|
-
border: var(--border-w) solid var(--action-border);
|
|
3267
|
-
border-radius: var(--r-card);
|
|
3268
|
-
background: var(--action-bg);
|
|
3269
|
-
color: var(--action-ink);
|
|
3270
|
-
box-shadow: var(--shadow-float);
|
|
3339
|
+
padding: var(--callout-pad-y, var(--sp-3)) var(--callout-pad-x, var(--sp-4));
|
|
3340
|
+
border: var(--border-w) solid var(--callout-border, var(--action-border));
|
|
3341
|
+
border-radius: var(--callout-radius, var(--r-card));
|
|
3342
|
+
background: var(--callout-bg, var(--action-bg));
|
|
3343
|
+
color: var(--callout-ink, var(--action-ink));
|
|
3344
|
+
box-shadow: var(--callout-shadow, var(--shadow-float));
|
|
3271
3345
|
text-align: center;
|
|
3272
3346
|
}
|
|
3273
3347
|
.callout::after {
|
|
@@ -3284,16 +3358,16 @@ button { font: inherit; cursor: pointer; }
|
|
|
3284
3358
|
border-top: 0;
|
|
3285
3359
|
border-left: 0;
|
|
3286
3360
|
}
|
|
3287
|
-
.callout__title { font: var(--type-title); }
|
|
3288
|
-
.callout__sub { font: var(--type-caption); opacity: 0.7; }
|
|
3361
|
+
.callout__title { font: var(--callout-title-font, var(--type-title)); }
|
|
3362
|
+
.callout__sub { font: var(--callout-sub-font, var(--type-caption)); opacity: 0.7; }
|
|
3289
3363
|
/* ---- deck: swipe cards; the last child is on top ----
|
|
3290
3364
|
<div class="deck"><article class="card">…</article><article class="card">…</article><article class="card card--fold">front</article></div>
|
|
3291
3365
|
<div class="deck__actions"><button class="icon-btn">×</button><button class="icon-btn icon-btn--invert">✓</button></div> */
|
|
3292
|
-
.deck { display: grid; place-items: center; padding: var(--sp-4) var(--sp-4) var(--sp-6); }
|
|
3366
|
+
.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)); }
|
|
3293
3367
|
.deck > .card { grid-area: 1 / 1; width: min(100%, 20rem); min-height: 12.5rem; transition: rotate 0.3s ease, translate 0.3s ease; }
|
|
3294
3368
|
.deck > .card:nth-last-child(2) { rotate: -4deg; translate: calc(-1 * var(--sp-2)) var(--sp-2); opacity: 0.85; }
|
|
3295
3369
|
.deck > .card:nth-last-child(3) { rotate: 5deg; translate: var(--sp-3) var(--sp-4); opacity: 0.6; }
|
|
3296
3370
|
.deck > .card:nth-last-child(n + 4) { display: none; }
|
|
3297
|
-
.deck__actions { display: flex; justify-content: center; gap: var(--sp-8); }
|
|
3371
|
+
.deck__actions { display: flex; justify-content: center; gap: var(--deck-actions-gap, var(--sp-8)); }
|
|
3298
3372
|
.deck__actions > .icon-btn { width: var(--bar-h); height: var(--bar-h); }
|
|
3299
3373
|
|