@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.
Files changed (103) hide show
  1. package/README.md +12 -8
  2. package/css/actions.css +38 -27
  3. package/css/base.css +9 -9
  4. package/css/card.css +24 -23
  5. package/css/choice.css +50 -50
  6. package/css/forms.css +18 -18
  7. package/css/journey.css +16 -16
  8. package/css/layover.css +57 -60
  9. package/css/lists.css +52 -43
  10. package/css/media.css +96 -60
  11. package/css/numbers.css +41 -25
  12. package/css/people.css +11 -11
  13. package/css/sheet.css +28 -24
  14. package/css/stack.css +3 -3
  15. package/css/theme.template.css +6 -3
  16. package/dist/actions/Btn.d.ts +109 -108
  17. package/dist/actions/BtnRow.d.ts +2 -2
  18. package/dist/actions/Chip.d.ts +102 -101
  19. package/dist/actions/Dot.d.ts +2 -2
  20. package/dist/actions/Dropdown.d.ts +4 -4
  21. package/dist/actions/Fab.d.ts +2 -2
  22. package/dist/actions/IconBtn.d.ts +107 -106
  23. package/dist/actions/Link.d.ts +105 -104
  24. package/dist/actions/Segment.d.ts +3 -3
  25. package/dist/cardds.css +443 -369
  26. package/dist/cards/Card.d.ts +99 -98
  27. package/dist/cards/CardFoot.d.ts +2 -2
  28. package/dist/cards/CardHead.d.ts +2 -2
  29. package/dist/cards/CardParts.d.ts +7 -7
  30. package/dist/cards/Placeholder.d.ts +2 -2
  31. package/dist/cards/Stat.d.ts +7 -5
  32. package/dist/cards/Stat.js +2 -2
  33. package/dist/choice/Calendar.d.ts +2 -2
  34. package/dist/choice/Check.d.ts +3 -3
  35. package/dist/choice/DayStrip.d.ts +3 -3
  36. package/dist/choice/Mood.d.ts +2 -2
  37. package/dist/choice/Slider.d.ts +2 -2
  38. package/dist/forms/AddRow.d.ts +2 -2
  39. package/dist/forms/Composer.d.ts +2 -2
  40. package/dist/forms/Field.d.ts +2 -2
  41. package/dist/forms/FileBtn.d.ts +2 -2
  42. package/dist/forms/Pin.d.ts +2 -2
  43. package/dist/index.d.ts +2 -1
  44. package/dist/index.js +2 -1
  45. package/dist/journey/NoteRow.d.ts +2 -2
  46. package/dist/journey/Route.d.ts +2 -2
  47. package/dist/journey/TileBadge.d.ts +2 -2
  48. package/dist/layover/Banner.d.ts +2 -2
  49. package/dist/layover/Callout.d.ts +2 -2
  50. package/dist/layover/Deck.d.ts +3 -3
  51. package/dist/lists/Bubble.d.ts +3 -3
  52. package/dist/lists/Kv.d.ts +5 -5
  53. package/dist/lists/Legend.d.ts +3 -3
  54. package/dist/lists/Row.d.ts +14 -285
  55. package/dist/lists/Row.js +10 -3
  56. package/dist/lists/Timeline.d.ts +3 -3
  57. package/dist/media/MapArea.d.ts +2 -2
  58. package/dist/media/Mosaic.d.ts +3 -3
  59. package/dist/media/Postcard.d.ts +17 -7
  60. package/dist/media/Postcard.js +8 -5
  61. package/dist/media/Quote.d.ts +2 -2
  62. package/dist/media/Tile.d.ts +5 -4
  63. package/dist/numbers/Badge.d.ts +2 -2
  64. package/dist/numbers/Band.d.ts +3 -3
  65. package/dist/numbers/Bars.d.ts +3 -3
  66. package/dist/numbers/DotGrid.d.ts +2 -2
  67. package/dist/numbers/Picker.d.ts +3 -3
  68. package/dist/numbers/Progress.d.ts +9 -0
  69. package/dist/numbers/Progress.js +7 -0
  70. package/dist/numbers/Ring.d.ts +2 -2
  71. package/dist/numbers/Track.d.ts +3 -3
  72. package/dist/people/Avatar.d.ts +100 -99
  73. package/dist/people/AvatarPick.d.ts +3 -3
  74. package/dist/scaffold/ActionBar.d.ts +3 -3
  75. package/dist/scaffold/AppBar.d.ts +2 -2
  76. package/dist/scaffold/BaseContent.d.ts +3 -3
  77. package/dist/scaffold/Centre.d.ts +3 -3
  78. package/dist/scaffold/EmptyState.d.ts +2 -2
  79. package/dist/scaffold/FilterRow.d.ts +2 -2
  80. package/dist/scaffold/FloatBar.d.ts +105 -104
  81. package/dist/scaffold/PagerAt.d.ts +2 -2
  82. package/dist/scaffold/Screen.d.ts +100 -99
  83. package/dist/scaffold/TopBar.d.ts +2 -2
  84. package/dist/sheets/Drawer.d.ts +7 -7
  85. package/dist/sheets/Drawer.js +1 -1
  86. package/dist/sheets/Sheet.d.ts +3 -5
  87. package/dist/sheets/Sheet.js +2 -2
  88. package/dist/sheets/SheetBody.d.ts +2 -2
  89. package/dist/sheets/SheetHead.d.ts +2 -2
  90. package/dist/sheets/SheetStack.d.ts +5 -5
  91. package/dist/sheets/SheetStack.js +3 -3
  92. package/dist/sheets/SheetStage.d.ts +5 -7
  93. package/dist/sheets/SheetStage.js +3 -3
  94. package/dist/sheets/StackBack.d.ts +7 -0
  95. package/dist/sheets/StackBack.js +7 -0
  96. package/dist/sheets/Step.d.ts +2 -2
  97. package/dist/type/Icon.d.ts +22 -7
  98. package/dist/type/Icon.js +27 -4
  99. package/dist/type/Text.d.ts +7 -1
  100. package/dist/type/Text.js +2 -2
  101. package/package.json +3 -2
  102. package/dist/sheets/CardBack.d.ts +0 -7
  103. package/dist/sheets/CardBack.js +0 -7
package/README.md CHANGED
@@ -105,7 +105,7 @@ the card is the material, the sheet is what it is in the stack.
105
105
  <div class="sheet-stack sheet-stack--tap"> <!-- drop --tap for a static stack -->
106
106
  <article class="card card--2">
107
107
  <div class="card__head">
108
- <button class="icon-btn icon-btn--sm card__back" aria-label="Back">…</button>
108
+ <button class="icon-btn icon-btn--sm sheet-stack__back" aria-label="Back">…</button>
109
109
  <h2 class="t-h2 t-caps">Shopping</h2>
110
110
  <span class="icon-row">…</span> <!-- optional trailing icons -->
111
111
  </div>
@@ -125,7 +125,7 @@ the card is the material, the sheet is what it is in the stack.
125
125
  A tap opens any sheet over it; back drops it home.
126
126
  - **Five sheets max.** Slots are declared per `:nth-child` in `stack.css`; a
127
127
  sixth would render at slot 0, on top of the first.
128
- - **Every head needs its own `.card__back`** if the stack is `--tap`. It stays
128
+ - **Every head needs its own `.sheet-stack__back`** if the stack is `--tap`. It stays
129
129
  hidden until that sheet is open, then slides into a reserved header slot.
130
130
  - **No ids, no wiring.** `cardds.js` finds any `.sheet-stack--tap` on the page.
131
131
  - **`.card-stack` is gone.** That was this component's first name and the wrong
@@ -433,7 +433,7 @@ Demo: the *skeleton* block at the top of `elements.html`.
433
433
  padding. Two boxes hold sheets and the states
434
434
  mean the same in both: `.step` (an app screen, the bar outside — Anatomy)
435
435
  and `.sheet-stage` (a page-level overlay: a sheet over a map, the dim page
436
- sheet `--dim` + `--raised`, the ask `--ask` + `.modal` = `__lift` + `__drawer`).
436
+ stage `--dim` + `.modal` = `__lift` + `__drawer` — a sheet always stands on the floor, never raised).
437
437
  A stage keeps the top bar's line free (`--_top`), so `--full` stops under
438
438
  `--header-h` and keeps its corners. Both sheet tokens are published — a
439
439
  sheet with a taller head retunes `--sheet-peek` — and registered in
@@ -467,7 +467,7 @@ A project retunes cardds from ONE file of its own, and nothing else. Copy
467
467
  <html data-palette="own"> <!-- colours of your own = coloured mode; any value no stock palette uses -->
468
468
  ```
469
469
 
470
- That file is the whole public surface:
470
+ That file is where a project's look lives. Bases and roles:
471
471
 
472
472
  | what | tokens | moves |
473
473
  |---|---|---|
@@ -481,10 +481,14 @@ That file is the whole public surface:
481
481
 
482
482
  Rules:
483
483
 
484
- - **A base, never a multiplier.** Every size token is `calc(base × multiplier)`
485
- in `tokens.css`; the multipliers are the system's proportions. A project
486
- never sets a derived token (`--sp-4`, `--fs-h1`, `--r-card`, `--tap`…), a
487
- private one (`--_*`), or a published one (`--card-pad`, `--bar-reserve`, `--sheet-*`).
484
+ - **Three levels, widest first — use the widest that says what you mean.**
485
+ 1. a **base** moves a whole family (`--sp-base`, `--fs-base`, `--r-base`, `--tap-base`, the five `--color-*`): the system keeps its proportions.
486
+ 2. a **role / root token** moves one meaning everywhere (`--accent`, `--card-1-bg`, `--btn-h`, `--chip-h`, `--header-h`, `--screen-w`, `--motion-sheet`…).
487
+ 3. a **knob** moves ONE component (`--btn-pad-x`, `--chip-bg`, `--icon-btn-size`, `--appbar-pad-x`…) — on `:root` for everywhere, or on any element for
488
+ the components inside it. A knob is declared nowhere; its default is the fallback in the rule. The full list is generated from the CSS:
489
+ [`docs/knobs.md`](docs/knobs.md) (`npm run knobs`). Missing the knob you need? That is a gap in cardds — report it; it is added as a knob, never as your special case.
490
+ - **Never a multiplier, never a step of the scale** (`--sp-4`, `--fs-h1`, `--r-card` — retune the base, or the component's knob), never a private
491
+ (`--_*`) or a published value (`--card-pad`, `--bar-reserve`, `--sheet-*`: other rules do arithmetic with them).
488
492
  - **On `:root` only.** The derived tokens are computed where they are declared
489
493
  (`:root`), so a base set on a subtree does not reach them.
490
494
  - **After `cardds.css`.** A stock palette's block and a project's `:root` weigh
package/css/actions.css CHANGED
@@ -3,6 +3,14 @@
3
3
  topbar, chips, buttons, selects, bottom CTA pack
4
4
  ============================================================ */
5
5
 
6
+ /* KNOBS (Lh 2026-09-21) — cardds is a framework: a project adjusts ONE component from outside, without touching this file.
7
+ A knob is a custom property a rule reads WITH ITS DEFAULT AS THE FALLBACK — `padding-inline: var(--btn-pad-x, var(--sp-6))` —
8
+ so it is declared nowhere: unset, nothing moves (tests/style-snapshot proves it); set in the project's theme.css it retunes
9
+ that component everywhere; set on any element it retunes the components inside that element only. Names: --<component>-<what>
10
+ (--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).
11
+ The sizes that already were root tokens (--btn-h, --chip-h, --bar-h, --header-h, --fab…) are knobs too — set them the same way.
12
+ 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. */
13
+
6
14
  /* ---- the bars across the top of a screen: one height, whatever they carry ----
7
15
  .topbar (screen title) and .appbar (app context) are the same bar with
8
16
  different contents, so the height is declared here once rather than left to
@@ -13,8 +21,8 @@
13
21
  .topbar, .appbar {
14
22
  display: flex;
15
23
  align-items: center;
16
- gap: var(--sp-3);
17
- padding-block: var(--sp-2);
24
+ gap: var(--bar-gap, var(--sp-3));
25
+ padding-block: var(--bar-pad-y, var(--sp-2));
18
26
  min-height: var(--header-h);
19
27
  }
20
28
 
@@ -43,9 +51,9 @@
43
51
  top: 0;
44
52
  max-width: var(--screen-w);
45
53
  margin-inline: auto;
46
- 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) */
54
+ 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) */
47
55
  z-index: 40; /* topmost — card animations never cover it */
48
- background: var(--surface); /* solid: sliding cards never show through */
56
+ background: var(--appbar-bg, var(--surface)); /* solid: sliding cards never show through */
49
57
  }
50
58
  /* fixed, so it covers the top of the screen it sits in — reserve its height
51
59
  there, from the same token, the way the bottom bars reserve theirs. The
@@ -62,19 +70,19 @@
62
70
 
63
71
  /* ---- icon button (round) ---- */
64
72
  .icon-btn {
65
- width: var(--tap); height: var(--tap);
73
+ width: var(--icon-btn-size, var(--tap)); height: var(--icon-btn-size, var(--tap));
66
74
  flex: none;
67
- border-radius: var(--r-chip);
68
- border: var(--border-w) solid var(--outline); /* see .btn — same reason */
69
- background: var(--action-bg);
70
- color: var(--action-ink);
75
+ border-radius: var(--icon-btn-radius, var(--r-chip));
76
+ border: var(--border-w) solid var(--icon-btn-border, var(--outline)); /* see .btn — same reason */
77
+ background: var(--icon-btn-bg, var(--action-bg));
78
+ color: var(--icon-btn-ink, var(--action-ink));
71
79
  display: grid;
72
80
  place-items: center;
73
81
  font-size: var(--fs-lg);
74
82
  line-height: 1;
75
83
  }
76
- .icon-btn--invert { background: var(--accent); color: var(--on-accent); border-color: transparent; }
77
- .icon-btn--sm { width: var(--tap-sm); height: var(--tap-sm); font-size: var(--fs-title); }
84
+ .icon-btn--invert { background: var(--icon-btn-invert-bg, var(--accent)); color: var(--icon-btn-invert-ink, var(--on-accent)); border-color: transparent; }
85
+ .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); }
78
86
  /* fab: the square floating action, bottom-right of a stage or screen */
79
87
  .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; }
80
88
 
@@ -83,21 +91,21 @@
83
91
  /* --outline, not --action-border: same reason as .chip — on a palette where
84
92
  --card-1-bg equals --action-bg a transparent border makes a plain button
85
93
  invisible on a white card. --primary and --quiet set their own edge. */
86
- border: var(--border-w) solid var(--outline);
87
- background: var(--action-bg);
88
- color: var(--action-ink);
89
- border-radius: var(--r-chip);
90
- padding: var(--sp-4) var(--sp-6);
94
+ border: var(--border-w) solid var(--btn-border, var(--outline));
95
+ background: var(--btn-bg, var(--action-bg));
96
+ color: var(--btn-ink, var(--action-ink));
97
+ border-radius: var(--btn-radius, var(--r-chip));
98
+ padding: var(--btn-pad-y, var(--sp-4)) var(--btn-pad-x, var(--sp-6));
91
99
  font: var(--type-label);
92
- font-size: var(--fs-md);
100
+ font-size: var(--btn-font-size, var(--fs-md));
93
101
  display: inline-flex;
94
102
  align-items: center;
95
103
  justify-content: center;
96
- gap: var(--sp-2);
104
+ gap: var(--btn-gap, var(--sp-2));
97
105
  min-height: var(--btn-h);
98
106
  text-decoration: none; /* .btn works as <a> too */
99
107
  }
100
- .btn--primary { background: var(--accent); color: var(--on-accent); border-color: transparent; }
108
+ .btn--primary { background: var(--btn-primary-bg, var(--accent)); color: var(--btn-primary-ink, var(--on-accent)); border-color: transparent; }
101
109
  /* quiet keeps a faint outline so it still reads as a button on colored palettes;
102
110
  it is ink on whatever it sits on — inherit, or on the dark slot (card--3) it
103
111
  would take the light-card action ink and vanish */
@@ -117,25 +125,25 @@
117
125
  .chip {
118
126
  display: inline-flex;
119
127
  align-items: center;
120
- gap: var(--sp-2);
121
- border-radius: var(--r-chip);
128
+ gap: var(--chip-gap, var(--sp-2));
129
+ border-radius: var(--chip-radius, var(--r-chip));
122
130
  /* --outline, not --action-border: a chip is a control, and on a palette
123
131
  where --card-1-bg == --action-bg (field, roast, hydro) a transparent
124
132
  border makes it invisible on a white card. Same edge fields, checks,
125
133
  toggles, tiles and the dropdown already use. */
126
- border: var(--border-w) solid var(--outline);
127
- background: var(--action-bg);
128
- color: var(--action-ink);
129
- padding: var(--sp-2) var(--sp-3);
134
+ border: var(--border-w) solid var(--chip-border, var(--outline));
135
+ background: var(--chip-bg, var(--action-bg));
136
+ color: var(--chip-ink, var(--action-ink));
137
+ padding: var(--chip-pad-y, var(--sp-2)) var(--chip-pad-x, var(--sp-3));
130
138
  font: var(--type-label);
131
- font-size: var(--fs-sm);
139
+ font-size: var(--chip-font-size, var(--fs-sm));
132
140
  min-height: var(--chip-h);
133
141
  text-decoration: none;
134
142
  justify-self: start; /* as a grid child it hugs its text, never stretches */
135
143
  }
136
144
  .dot { width: var(--dot); height: var(--dot); flex: none; border-radius: 50%; background: var(--dot-a); } /* signal dot: chips, legends */
137
145
  .dot--b { background: var(--dot-b); }
138
- .chip--on { background: var(--accent); color: var(--on-accent); border-color: transparent; }
146
+ .chip--on { background: var(--chip-on-bg, var(--accent)); color: var(--chip-on-ink, var(--on-accent)); border-color: transparent; }
139
147
  /* dark-slot inversion lives in card.css (.card--3 :is(...)) */
140
148
 
141
149
  .chip-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
@@ -271,6 +279,9 @@
271
279
 
272
280
  /* the first round button sits at the left edge, the last at the right; a skip link between them stays centred */
273
281
  :is(.action-bar--pager, .action-bar__tier--pager) > .icon-btn:first-child { margin-right: auto; }
282
+ /* …and whatever goes BETWEEN the two (a "skip" link) stands in the middle: the bar places it, never the link itself */
283
+ :is(.action-bar--pager, .action-bar__tier--pager) > :not(.icon-btn):not(:first-child):not(:last-child) { margin-inline: auto; }
284
+ :is(.action-bar--pager, .action-bar__tier--pager):has(> :nth-child(3)) > .icon-btn:first-child { margin-right: 0; }
274
285
 
275
286
  /* .screen's default bottom padding is a constant; derive the reserve from the
276
287
  bar instead, so a taller button (a longer label, a script with more leading)
package/css/base.css CHANGED
@@ -43,10 +43,10 @@ img, svg { display: block; max-width: 100%; }
43
43
  button { font: inherit; cursor: pointer; }
44
44
 
45
45
  /* icon from icons.svg sprite: <svg class="icon"><use href="icons.svg#bell"/></svg> */
46
- .icon { width: var(--icon); height: var(--icon); flex: none; }
47
- .icon--sm { width: var(--icon-sm); height: var(--icon-sm); }
48
- .icon--xs { width: var(--icon-xs); height: var(--icon-xs); } /* inside captions, chips, links */
49
- .icon--lg { width: var(--icon-lg); height: var(--icon-lg); } /* empty states */
46
+ .icon { width: var(--icon-size, var(--icon)); height: var(--icon-size, var(--icon)); flex: none; }
47
+ .icon--sm { width: var(--icon-sm-size, var(--icon-sm)); height: var(--icon-sm-size, var(--icon-sm)); }
48
+ .icon--xs { width: var(--icon-xs-size, var(--icon-xs)); height: var(--icon-xs-size, var(--icon-xs)); } /* inside captions, chips, links */
49
+ .icon--lg { width: var(--icon-lg-size, var(--icon-lg)); height: var(--icon-lg-size, var(--icon-lg)); } /* empty states */
50
50
 
51
51
  /* mobile screen column — the standard horizontal inset, and nothing vertical.
52
52
  Top and bottom are zero on purpose: vertical room is only ever a RESERVE for
@@ -113,9 +113,9 @@ button { font: inherit; cursor: pointer; }
113
113
  In a .step it is the reading block and step.css adds the flex/scroll rule ---- */
114
114
  .base-content {
115
115
  display: grid;
116
- gap: var(--sp-3);
116
+ gap: var(--base-content-gap, var(--sp-3));
117
117
  align-content: start; /* a tall column never stretches the rows — the words hug the top */
118
- padding-block: var(--sp-4);
118
+ padding-block: var(--base-content-pad-y, var(--sp-4));
119
119
  }
120
120
  .base-content > .t-body { color: var(--on-surface-muted); max-width: 34ch; }
121
121
  .base-content .cta-pack {
@@ -131,9 +131,9 @@ button { font: inherit; cursor: pointer; }
131
131
  display: grid;
132
132
  justify-items: center;
133
133
  text-align: center;
134
- gap: var(--sp-2);
135
- padding: var(--sp-12) var(--sp-6);
136
- 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 */
134
+ gap: var(--empty-state-gap, var(--sp-2));
135
+ padding: var(--empty-state-pad-y, var(--sp-12)) var(--empty-state-pad-x, var(--sp-6));
136
+ 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 */
137
137
  }
138
138
  .empty-state__action { margin-top: var(--sp-2); }
139
139
  /* inside a card (a section's "nothing yet"): the card's ink */
package/css/card.css CHANGED
@@ -11,7 +11,7 @@
11
11
  background: var(--card-bg);
12
12
  color: var(--card-ink);
13
13
  border: var(--border-w) solid var(--card-border);
14
- border-radius: var(--r-card);
14
+ border-radius: var(--card-radius, var(--r-card));
15
15
  padding: var(--card-pad);
16
16
  display: grid;
17
17
  gap: var(--card-gap);
@@ -42,8 +42,8 @@
42
42
  flex-wrap: wrap; /* the caption / overline take a line of their own — no wrapper div */
43
43
  align-items: center;
44
44
  justify-content: space-between;
45
- column-gap: var(--sp-3);
46
- row-gap: var(--sp-0);
45
+ column-gap: var(--card-head-col-gap, var(--sp-3));
46
+ row-gap: var(--card-head-row-gap, var(--sp-0));
47
47
  }
48
48
  /* flat head markup (Lh 2026-09-16 — no <p>, no wrapper, only padding and gap tokens):
49
49
  <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>
@@ -54,8 +54,8 @@
54
54
  .card__head > .t-overline { order: -1; }
55
55
  .card__head--top { align-items: flex-start; }
56
56
 
57
- .card__media { border-radius: var(--r-media); overflow: hidden; position: relative; } /* relative: corner badges */
58
- .card__end { text-align: right; display: grid; gap: var(--sp-0); justify-items: end; } /* trailing value + caption */
57
+ .card__media { border-radius: var(--card-media-radius, var(--r-media)); overflow: hidden; position: relative; } /* relative: corner badges */
58
+ .card__end { text-align: right; display: grid; gap: var(--card-end-gap, var(--sp-0)); justify-items: end; } /* trailing value + caption */
59
59
  .card__media > img { width: 100%; height: auto; object-fit: cover; }
60
60
 
61
61
  /* ---- the card skeleton: head · content · foot ----
@@ -119,7 +119,7 @@
119
119
  /* foot: the card's action buttons on one line. One button takes the width,
120
120
  up to four share it in even parts — so the gaps read even too — and from
121
121
  three on they drop their side padding so the labels keep the room. */
122
- .card__foot { display: flex; gap: var(--sp-2); align-items: center; }
122
+ .card__foot { display: flex; gap: var(--card-foot-gap, var(--sp-2)); align-items: center; }
123
123
  .card__foot > .btn { flex: 1 1 0; min-width: 0; }
124
124
  .card__foot:has(> :nth-child(3)) > .btn { padding-inline: var(--sp-2); }
125
125
 
@@ -253,48 +253,49 @@
253
253
  .screen__centre > .pager__at { justify-self: center; }
254
254
 
255
255
  /* compact card inside a card (nested note, promo) */
256
- .card--sm { padding: var(--sp-4); --card-gap: var(--sp-2); border-radius: var(--r-card); }
256
+ .card--sm { padding: var(--card-sm-pad, var(--sp-4)); --card-gap: var(--sp-2); border-radius: var(--card-sm-radius, var(--r-card)); }
257
257
 
258
258
  /* the sheet stack (.sheet-stack) — sheets stacked, heads peeking — lives in
259
259
  stack.css; a stacked sheet is a plain .card, the material */
260
260
 
261
261
  /* trailing icon cluster in a card header */
262
- .icon-row { display: flex; align-items: center; gap: var(--sp-4); opacity: 0.85; }
262
+ .icon-row { display: flex; align-items: center; gap: var(--icon-row-gap, var(--sp-4)); opacity: 0.85; }
263
263
 
264
264
  /* ---- card list: rows, each row its own boundary ---- */
265
- .card-list { display: grid; gap: var(--sp-3); }
265
+ .card-list { display: grid; gap: var(--card-list-gap, var(--sp-3)); }
266
266
 
267
267
  .card--row {
268
- border-radius: var(--r-card);
269
- padding: var(--sp-4);
268
+ border-radius: var(--card-row-radius, var(--r-card));
269
+ padding: var(--card-row-pad, var(--sp-4));
270
270
  display: flex;
271
271
  align-items: center;
272
- gap: var(--sp-3);
272
+ gap: var(--card-row-gap, var(--sp-3));
273
273
  }
274
274
  .card--row .card__body { flex: 1; display: grid; gap: var(--sp-0); }
275
275
 
276
276
  /* leading thumb/icon in a row */
277
277
  .card__thumb {
278
- width: var(--tap-sm); height: var(--tap-sm);
278
+ width: var(--card-thumb-size, var(--tap-sm)); height: var(--card-thumb-size, var(--tap-sm));
279
279
  flex: none;
280
- border-radius: var(--r-control);
280
+ border-radius: var(--card-thumb-radius, var(--r-control));
281
281
  border: var(--border-w) solid currentColor;
282
282
  display: grid;
283
283
  place-items: center;
284
- font: var(--type-label);
284
+ font: var(--card-thumb-font, var(--type-label));
285
285
  opacity: 0.85;
286
286
  }
287
287
 
288
288
  /* ---- inline stat block (revenue-style) ---- */
289
- .stat { display: grid; gap: var(--sp-1); }
290
- .stat__row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
289
+ .stat { display: grid; gap: var(--stat-gap, var(--sp-1)); }
290
+ .stat--centre { text-align: center; justify-items: center; } /* the one number of a sheet or a stage, under a centred avatar */
291
+ .stat__row { display: flex; align-items: center; justify-content: space-between; gap: var(--stat-row-gap, var(--sp-3)); }
291
292
 
292
293
  .delta {
293
- font: var(--type-caption);
294
+ font: var(--delta-font, var(--type-caption));
294
295
  font-weight: 700;
295
- padding: var(--sp-1) var(--sp-2);
296
- border-radius: var(--r-chip);
297
- background: var(--card-3-bg);
298
- color: var(--card-3-ink);
296
+ padding: var(--delta-pad-y, var(--sp-1)) var(--delta-pad-x, var(--sp-2));
297
+ border-radius: var(--delta-radius, var(--r-chip));
298
+ background: var(--delta-bg, var(--card-3-bg));
299
+ color: var(--delta-ink, var(--card-3-ink));
299
300
  }
300
- .delta--neg { background: var(--neg); color: var(--card-3-ink); } /* light ink on the signal color in every slot */
301
+ .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 */
package/css/choice.css CHANGED
@@ -12,13 +12,13 @@
12
12
  <input type="checkbox" class="check" aria-label="Done"> */
13
13
  .check {
14
14
  appearance: none;
15
- width: var(--tap);
16
- height: var(--tap);
15
+ width: var(--check-size, var(--tap));
16
+ height: var(--check-size, var(--tap));
17
17
  flex: none;
18
18
  margin: 0;
19
19
  border-radius: 50%;
20
- border: var(--border-w) solid var(--outline);
21
- background: var(--action-bg);
20
+ border: var(--border-w) solid var(--check-border, var(--outline));
21
+ background: var(--check-bg, var(--action-bg));
22
22
  display: grid;
23
23
  place-items: center;
24
24
  cursor: pointer;
@@ -35,7 +35,7 @@
35
35
  .check:checked { background: var(--accent); border-color: transparent; }
36
36
  .check:checked::after { opacity: 1; }
37
37
  .check:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
38
- .check--sm { width: var(--tap-sm); height: var(--tap-sm); }
38
+ .check--sm { width: var(--check-sm-size, var(--tap-sm)); height: var(--check-sm-size, var(--tap-sm)); }
39
39
 
40
40
  /* ---- toggle: big switch ----
41
41
  <input type="checkbox" role="switch" class="toggle" aria-label="Private"> */
@@ -48,9 +48,9 @@
48
48
  height: var(--_h);
49
49
  flex: none;
50
50
  margin: 0;
51
- border-radius: var(--r-chip);
52
- border: var(--border-w) solid var(--outline);
53
- background: var(--action-bg);
51
+ border-radius: var(--toggle-radius, var(--r-chip));
52
+ border: var(--border-w) solid var(--toggle-border, var(--outline));
53
+ background: var(--toggle-bg, var(--action-bg));
54
54
  position: relative;
55
55
  cursor: pointer;
56
56
  transition: background 0.2s ease;
@@ -79,7 +79,7 @@
79
79
  .chip-grid {
80
80
  display: grid;
81
81
  grid-template-columns: repeat(auto-fill, minmax(min(5.5rem, 100%), 1fr));
82
- gap: var(--sp-2);
82
+ gap: var(--chip-grid-gap, var(--sp-2));
83
83
  }
84
84
  /* .chip hugs its text as a loose grid child (justify-self: start); inside a
85
85
  chip grid the whole point is equal, full-width pick targets, so stretch.
@@ -106,7 +106,7 @@
106
106
 
107
107
  /* ---- day strip: a week, one day selected ----
108
108
  <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> */
109
- .day-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--sp-1); }
109
+ .day-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--day-strip-gap, var(--sp-1)); }
110
110
  .day {
111
111
  display: grid;
112
112
  /* three rows, always: name · number · dot. The dot's row is reserved even on
@@ -116,33 +116,33 @@
116
116
  grid-template-rows: auto auto var(--dot-xs);
117
117
  align-content: center; /* the group sits centred in the cell, not spread out */
118
118
  justify-items: center;
119
- gap: var(--sp-0);
119
+ gap: var(--day-gap, var(--sp-0));
120
120
  min-width: 0;
121
- min-height: var(--bar-h);
122
- padding-block: var(--sp-3); /* equal air top and bottom, so the selected pill
121
+ min-height: var(--day-min-h, var(--bar-h));
122
+ padding-block: var(--day-pad-y, var(--sp-3)); /* equal air top and bottom, so the selected pill
123
123
  is the same shape on every day */
124
124
  padding-inline: 0;
125
125
  border: 0;
126
- border-radius: var(--r-chip);
126
+ border-radius: var(--day-radius, var(--r-chip));
127
127
  background: transparent;
128
128
  color: inherit;
129
129
  opacity: 0.6;
130
130
  }
131
- .day__name { font: var(--type-caption); }
131
+ .day__name { font: var(--day-name-font, var(--type-caption)); }
132
132
  .day__num { font: 700 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
133
- .day[aria-selected="true"] { background: var(--accent); color: var(--on-accent); opacity: 1; }
134
- .day__dot { grid-row: 3; align-self: center; width: var(--dot-xs); height: var(--dot-xs); border-radius: 50%; background: currentColor; }
133
+ .day[aria-selected="true"] { background: var(--day-selected-bg, var(--accent)); color: var(--day-selected-ink, var(--on-accent)); opacity: 1; }
134
+ .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; }
135
135
 
136
136
  /* ---- calendar: month grid, 44px cells, one accent day, optional marks ----
137
137
  <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> */
138
- .calendar { display: grid; gap: var(--sp-3); }
139
- .calendar__head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
140
- .calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--sp-1); justify-items: center; }
141
- .calendar__wd { font: var(--type-overline); letter-spacing: var(--track-overline); text-transform: uppercase; color: var(--card-muted); padding-block: var(--sp-2); }
138
+ .calendar { display: grid; gap: var(--calendar-gap, var(--sp-3)); }
139
+ .calendar__head { display: flex; align-items: center; justify-content: space-between; gap: var(--calendar-head-gap, var(--sp-2)); }
140
+ .calendar__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--calendar-grid-gap, var(--sp-1)); justify-items: center; }
141
+ .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)); }
142
142
  .card--3 .calendar__wd { color: inherit; opacity: 0.7; }
143
143
  .calendar__day {
144
144
  width: 100%;
145
- max-width: var(--tap);
145
+ max-width: var(--calendar-day-max-w, var(--tap));
146
146
  aspect-ratio: 1;
147
147
  display: grid;
148
148
  place-items: center;
@@ -154,9 +154,9 @@
154
154
  font-variant-numeric: tabular-nums;
155
155
  grid-column-start: var(--start, auto); /* --start on the 1st: weekday column 1–7 */
156
156
  }
157
- .calendar__day--mark { background: var(--accent-soft); }
158
- .calendar__day--mark2 { background: var(--accent-mid); color: var(--on-accent); }
159
- .calendar__day[aria-selected="true"] { background: var(--accent); color: var(--on-accent); font-weight: 700; }
157
+ .calendar__day--mark { background: var(--calendar-day-mark-bg, var(--accent-soft)); }
158
+ .calendar__day--mark2 { background: var(--calendar-day-mark2-bg, var(--accent-mid)); color: var(--calendar-day-mark2-ink, var(--on-accent)); }
159
+ .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; }
160
160
  .calendar__day:disabled { opacity: 0.3; }
161
161
 
162
162
  /* ---- mood: five dots in a pill, the picked one grows ----
@@ -164,19 +164,19 @@
164
164
  .mood {
165
165
  margin: 0;
166
166
  min-inline-size: 0; /* fieldset UA default is min-content — it must shrink with the card */
167
- padding: var(--sp-1);
168
- border: var(--border-w) solid var(--outline);
169
- border-radius: var(--r-chip);
170
- background: var(--action-bg);
171
- color: var(--action-ink);
167
+ padding: var(--mood-pad, var(--sp-1));
168
+ border: var(--border-w) solid var(--mood-border, var(--outline));
169
+ border-radius: var(--mood-radius, var(--r-chip));
170
+ background: var(--mood-bg, var(--action-bg));
171
+ color: var(--mood-ink, var(--action-ink));
172
172
  display: flex;
173
173
  justify-content: space-between;
174
174
  }
175
- .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; }
175
+ .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; }
176
176
  .mood__opt > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
177
177
  .mood__dot {
178
- width: var(--icon-xs);
179
- height: var(--icon-xs);
178
+ width: var(--mood-dot-size, var(--icon-xs));
179
+ height: var(--mood-dot-size, var(--icon-xs));
180
180
  border-radius: 50%;
181
181
  background: var(--_mood, currentColor);
182
182
  transition: scale 0.2s ease, box-shadow 0.2s ease;
@@ -191,18 +191,18 @@
191
191
 
192
192
  /* ---- pin: big code entry, one digit per pill ----
193
193
  <div class="pin"><input class="pin__cell" inputmode="numeric" maxlength="1" placeholder=" ">×4</div> */
194
- .pin { display: flex; gap: var(--sp-3); }
194
+ .pin { display: flex; gap: var(--pin-gap, var(--sp-3)); }
195
195
  .pin__cell {
196
196
  flex: 1;
197
197
  min-width: 0;
198
- max-width: var(--fab);
198
+ max-width: var(--pin-cell-max-w, var(--fab));
199
199
  aspect-ratio: 2 / 3;
200
- border: var(--border-w) solid var(--outline);
201
- border-radius: var(--r-chip);
202
- background: var(--action-bg);
203
- color: var(--action-ink);
200
+ border: var(--border-w) solid var(--pin-cell-border, var(--outline));
201
+ border-radius: var(--pin-cell-radius, var(--r-chip));
202
+ background: var(--pin-cell-bg, var(--action-bg));
203
+ color: var(--pin-cell-ink, var(--action-ink));
204
204
  text-align: center;
205
- font: var(--type-stat);
205
+ font: var(--pin-cell-font, var(--type-stat));
206
206
  font-variant-numeric: tabular-nums;
207
207
  caret-color: currentColor;
208
208
  }
@@ -212,7 +212,7 @@
212
212
  /* ---- composer: big text area with a bottom bar (icon · hint · send) ----
213
213
  <div class="composer"><textarea class="composer__input" placeholder="What do you need?"></textarea>
214
214
  <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> */
215
- .composer { display: grid; gap: var(--sp-3); }
215
+ .composer { display: grid; gap: var(--composer-gap, var(--sp-3)); }
216
216
  .composer__input {
217
217
  width: 100%;
218
218
  min-height: 11rem;
@@ -226,8 +226,8 @@
226
226
  outline: none;
227
227
  }
228
228
  .composer__input::placeholder { color: inherit; opacity: 0.4; }
229
- .composer__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
230
- .composer__hint { font: var(--type-caption); color: var(--card-muted); }
229
+ .composer__bar { display: flex; align-items: center; justify-content: space-between; gap: var(--composer-bar-gap, var(--sp-3)); }
230
+ .composer__hint { font: var(--composer-hint-font, var(--type-caption)); color: var(--composer-hint-ink, var(--card-muted)); }
231
231
  .card--3 .composer__hint { color: inherit; opacity: 0.7; }
232
232
 
233
233
  /* ---- slider: one value along a track, a thumb-size knob ----
@@ -236,7 +236,7 @@
236
236
  slider does, the input's aria-label is enough. The track is a hairline rail;
237
237
  the knob is a full --tap, and the whole range is --tap tall, so the thumb has
238
238
  a real target anywhere along it. The value is the input's own. */
239
- .slider { display: grid; gap: var(--sp-1); flex: 1; min-width: 0; position: relative; } /* relative: the glyph rides the knob */
239
+ .slider { display: grid; gap: var(--slider-gap, var(--sp-1)); flex: 1; min-width: 0; position: relative; } /* relative: the glyph rides the knob */
240
240
  /* ---- the sample glyph: one letter IN the knob, on-accent, at the type size
241
241
  the slider sets — so a text-size slider shows the size before anything is
242
242
  read (Lh, 2026-09-12). cardds.js publishes the knob's place on the .slider
@@ -247,8 +247,8 @@
247
247
  position: absolute;
248
248
  bottom: 0;
249
249
  left: calc(var(--tap) / 2 + (100% - var(--tap)) * var(--slider-at, 0));
250
- width: var(--tap);
251
- height: var(--tap);
250
+ width: var(--slider-glyph-size, var(--tap));
251
+ height: var(--slider-glyph-size, var(--tap));
252
252
  /* the eye centres the INK, not the line box: a letter that stands on the
253
253
  baseline (ก) has no descender, while the face's box keeps 0.30em under it
254
254
  (fonts.css) — so the ink lands low when the box is centred; lifted 0.15em
@@ -256,15 +256,15 @@
256
256
  translate: -50% -0.15em;
257
257
  display: grid;
258
258
  place-items: center;
259
- font: var(--type-h2);
260
- color: var(--on-accent);
259
+ font: var(--slider-glyph-font, var(--type-h2));
260
+ color: var(--slider-glyph-ink, var(--on-accent));
261
261
  pointer-events: none;
262
262
  }
263
- .slider__label { font: var(--type-label); }
263
+ .slider__label { font: var(--slider-label-font, var(--type-label)); }
264
264
  .slider__range {
265
265
  appearance: none;
266
266
  width: 100%;
267
- height: var(--tap);
267
+ height: var(--slider-range-h, var(--tap));
268
268
  margin: 0;
269
269
  background: transparent;
270
270
  cursor: pointer;
package/css/forms.css CHANGED
@@ -7,13 +7,13 @@
7
7
  .field {
8
8
  display: flex;
9
9
  align-items: center;
10
- gap: var(--sp-2);
11
- border: var(--border-w) solid var(--action-border);
12
- border-radius: var(--r-control);
13
- background: var(--action-bg);
14
- color: var(--action-ink);
15
- padding-inline: var(--sp-4);
16
- min-height: var(--tap);
10
+ gap: var(--field-gap, var(--sp-2));
11
+ border: var(--border-w) solid var(--field-border, var(--action-border));
12
+ border-radius: var(--field-radius, var(--r-control));
13
+ background: var(--field-bg, var(--action-bg));
14
+ color: var(--field-ink, var(--action-ink));
15
+ padding-inline: var(--field-pad-x, var(--sp-4));
16
+ min-height: var(--field-min-h, var(--tap));
17
17
  min-width: 0;
18
18
  }
19
19
 
@@ -30,7 +30,7 @@
30
30
  .field input::placeholder { color: inherit; opacity: 0.55; }
31
31
 
32
32
  /* field with its action inside: date pick, send, go — the button sits flush right */
33
- .field--action { padding-right: var(--sp-1); min-height: var(--tap-lg); }
33
+ .field--action { padding-right: var(--sp-1); min-height: var(--field-action-min-h, var(--tap-lg)); }
34
34
  .field--action > .icon-btn { margin-left: auto; }
35
35
 
36
36
  /* colored palettes drop the visible outline elsewhere — fields keep one */
@@ -40,25 +40,25 @@
40
40
  .add-row {
41
41
  display: inline-flex;
42
42
  align-items: center;
43
- gap: var(--sp-2);
43
+ gap: var(--add-row-gap, var(--sp-2));
44
44
  border: 0;
45
45
  background: none;
46
46
  color: inherit;
47
- font: var(--type-label);
48
- font-size: var(--fs-body);
49
- padding: var(--sp-2) 0;
50
- min-height: var(--tap-sm);
47
+ font: var(--add-row-font, var(--type-label));
48
+ font-size: var(--add-row-font-size, var(--fs-body));
49
+ padding: var(--add-row-pad-y, var(--sp-2)) var(--add-row-pad-x, 0);
50
+ min-height: var(--add-row-min-h, var(--tap-sm));
51
51
  }
52
52
  .add-row__plus {
53
- width: var(--icon);
54
- height: var(--icon);
53
+ width: var(--add-row-plus-size, var(--icon));
54
+ height: var(--add-row-plus-size, var(--icon));
55
55
  flex: none;
56
56
  border-radius: 50%;
57
- background: var(--accent);
58
- color: var(--on-accent);
57
+ background: var(--add-row-plus-bg, var(--accent));
58
+ color: var(--add-row-plus-ink, var(--on-accent));
59
59
  display: grid;
60
60
  place-items: center;
61
- font-size: var(--fs-md);
61
+ font-size: var(--add-row-plus-font-size, var(--fs-md));
62
62
  line-height: 1;
63
63
  }
64
64
  .add-row__plus .icon { width: var(--icon-xs); height: var(--icon-xs); }