@usableapp/cardds 0.1.0

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 (170) hide show
  1. package/README.md +562 -0
  2. package/cardds.css +18 -0
  3. package/cardds.js +473 -0
  4. package/css/actions.css +292 -0
  5. package/css/base.css +128 -0
  6. package/css/card.css +283 -0
  7. package/css/choice.css +297 -0
  8. package/css/fonts.css +37 -0
  9. package/css/forms.css +64 -0
  10. package/css/journey.css +61 -0
  11. package/css/layover.css +243 -0
  12. package/css/lists.css +142 -0
  13. package/css/media.css +232 -0
  14. package/css/numbers.css +258 -0
  15. package/css/palette.css +112 -0
  16. package/css/people.css +79 -0
  17. package/css/sheet.css +295 -0
  18. package/css/stack.css +262 -0
  19. package/css/step.css +71 -0
  20. package/css/tokens.css +177 -0
  21. package/dist/actions/Btn.d.ts +311 -0
  22. package/dist/actions/Btn.js +14 -0
  23. package/dist/actions/BtnRow.d.ts +5 -0
  24. package/dist/actions/BtnRow.js +6 -0
  25. package/dist/actions/Chip.d.ts +328 -0
  26. package/dist/actions/Chip.js +25 -0
  27. package/dist/actions/Dot.d.ts +7 -0
  28. package/dist/actions/Dot.js +6 -0
  29. package/dist/actions/Dropdown.d.ts +34 -0
  30. package/dist/actions/Dropdown.js +19 -0
  31. package/dist/actions/Fab.d.ts +9 -0
  32. package/dist/actions/Fab.js +7 -0
  33. package/dist/actions/IconBtn.d.ts +308 -0
  34. package/dist/actions/IconBtn.js +8 -0
  35. package/dist/actions/Link.d.ts +301 -0
  36. package/dist/actions/Link.js +8 -0
  37. package/dist/actions/Segment.d.ts +11 -0
  38. package/dist/actions/Segment.js +10 -0
  39. package/dist/cardds.css +3068 -0
  40. package/dist/cards/Card.d.ts +310 -0
  41. package/dist/cards/Card.js +14 -0
  42. package/dist/cards/CardFoot.d.ts +5 -0
  43. package/dist/cards/CardFoot.js +6 -0
  44. package/dist/cards/CardHead.d.ts +21 -0
  45. package/dist/cards/CardHead.js +11 -0
  46. package/dist/cards/CardParts.d.ts +25 -0
  47. package/dist/cards/CardParts.js +26 -0
  48. package/dist/cards/Placeholder.d.ts +9 -0
  49. package/dist/cards/Placeholder.js +6 -0
  50. package/dist/cards/Stat.d.ts +17 -0
  51. package/dist/cards/Stat.js +14 -0
  52. package/dist/choice/Calendar.d.ts +23 -0
  53. package/dist/choice/Calendar.js +7 -0
  54. package/dist/choice/Check.d.ts +14 -0
  55. package/dist/choice/Check.js +10 -0
  56. package/dist/choice/DayStrip.d.ts +16 -0
  57. package/dist/choice/DayStrip.js +10 -0
  58. package/dist/choice/Mood.d.ts +11 -0
  59. package/dist/choice/Mood.js +6 -0
  60. package/dist/choice/Slider.d.ts +11 -0
  61. package/dist/choice/Slider.js +14 -0
  62. package/dist/cx.d.ts +2 -0
  63. package/dist/cx.js +2 -0
  64. package/dist/forms/AddRow.d.ts +5 -0
  65. package/dist/forms/AddRow.js +7 -0
  66. package/dist/forms/Composer.d.ts +13 -0
  67. package/dist/forms/Composer.js +6 -0
  68. package/dist/forms/Field.d.ts +17 -0
  69. package/dist/forms/Field.js +10 -0
  70. package/dist/forms/FileBtn.d.ts +18 -0
  71. package/dist/forms/FileBtn.js +10 -0
  72. package/dist/forms/Pin.d.ts +19 -0
  73. package/dist/forms/Pin.js +27 -0
  74. package/dist/index.d.ts +72 -0
  75. package/dist/index.js +75 -0
  76. package/dist/journey/NoteRow.d.ts +7 -0
  77. package/dist/journey/NoteRow.js +7 -0
  78. package/dist/journey/Route.d.ts +18 -0
  79. package/dist/journey/Route.js +8 -0
  80. package/dist/journey/TileBadge.d.ts +15 -0
  81. package/dist/journey/TileBadge.js +6 -0
  82. package/dist/layover/Banner.d.ts +9 -0
  83. package/dist/layover/Banner.js +6 -0
  84. package/dist/layover/Callout.d.ts +10 -0
  85. package/dist/layover/Callout.js +6 -0
  86. package/dist/layover/Deck.d.ts +9 -0
  87. package/dist/layover/Deck.js +10 -0
  88. package/dist/lists/Kv.d.ts +27 -0
  89. package/dist/lists/Kv.js +19 -0
  90. package/dist/lists/Legend.d.ts +11 -0
  91. package/dist/lists/Legend.js +11 -0
  92. package/dist/lists/Row.d.ts +319 -0
  93. package/dist/lists/Row.js +17 -0
  94. package/dist/lists/Timeline.d.ts +11 -0
  95. package/dist/lists/Timeline.js +10 -0
  96. package/dist/media/MapArea.d.ts +20 -0
  97. package/dist/media/MapArea.js +11 -0
  98. package/dist/media/Mosaic.d.ts +11 -0
  99. package/dist/media/Mosaic.js +10 -0
  100. package/dist/media/Postcard.d.ts +20 -0
  101. package/dist/media/Postcard.js +10 -0
  102. package/dist/media/Quote.d.ts +11 -0
  103. package/dist/media/Quote.js +6 -0
  104. package/dist/media/Tile.d.ts +320 -0
  105. package/dist/media/Tile.js +16 -0
  106. package/dist/numbers/Badge.d.ts +7 -0
  107. package/dist/numbers/Badge.js +6 -0
  108. package/dist/numbers/Band.d.ts +16 -0
  109. package/dist/numbers/Band.js +10 -0
  110. package/dist/numbers/Bars.d.ts +14 -0
  111. package/dist/numbers/Bars.js +10 -0
  112. package/dist/numbers/DotGrid.d.ts +12 -0
  113. package/dist/numbers/DotGrid.js +6 -0
  114. package/dist/numbers/Picker.d.ts +13 -0
  115. package/dist/numbers/Picker.js +18 -0
  116. package/dist/numbers/Ring.d.ts +15 -0
  117. package/dist/numbers/Ring.js +6 -0
  118. package/dist/numbers/Track.d.ts +12 -0
  119. package/dist/numbers/Track.js +10 -0
  120. package/dist/people/Avatar.d.ts +314 -0
  121. package/dist/people/Avatar.js +13 -0
  122. package/dist/people/AvatarPick.d.ts +18 -0
  123. package/dist/people/AvatarPick.js +10 -0
  124. package/dist/scaffold/ActionBar.d.ts +21 -0
  125. package/dist/scaffold/ActionBar.js +16 -0
  126. package/dist/scaffold/AppBar.d.ts +18 -0
  127. package/dist/scaffold/AppBar.js +10 -0
  128. package/dist/scaffold/Centre.d.ts +30 -0
  129. package/dist/scaffold/Centre.js +41 -0
  130. package/dist/scaffold/EmptyState.d.ts +14 -0
  131. package/dist/scaffold/EmptyState.js +8 -0
  132. package/dist/scaffold/FilterRow.d.ts +5 -0
  133. package/dist/scaffold/FilterRow.js +6 -0
  134. package/dist/scaffold/FloatBar.d.ts +310 -0
  135. package/dist/scaffold/FloatBar.js +15 -0
  136. package/dist/scaffold/PagerAt.d.ts +11 -0
  137. package/dist/scaffold/PagerAt.js +6 -0
  138. package/dist/scaffold/Screen.d.ts +299 -0
  139. package/dist/scaffold/Screen.js +11 -0
  140. package/dist/scaffold/Section.d.ts +12 -0
  141. package/dist/scaffold/Section.js +13 -0
  142. package/dist/scaffold/TopBar.d.ts +15 -0
  143. package/dist/scaffold/TopBar.js +9 -0
  144. package/dist/sheets/CardBack.d.ts +7 -0
  145. package/dist/sheets/CardBack.js +7 -0
  146. package/dist/sheets/Drawer.d.ts +17 -0
  147. package/dist/sheets/Drawer.js +16 -0
  148. package/dist/sheets/Sheet.d.ts +21 -0
  149. package/dist/sheets/Sheet.js +13 -0
  150. package/dist/sheets/SheetBody.d.ts +5 -0
  151. package/dist/sheets/SheetBody.js +6 -0
  152. package/dist/sheets/SheetHead.d.ts +13 -0
  153. package/dist/sheets/SheetHead.js +6 -0
  154. package/dist/sheets/SheetStack.d.ts +21 -0
  155. package/dist/sheets/SheetStack.js +14 -0
  156. package/dist/sheets/SheetStage.d.ts +20 -0
  157. package/dist/sheets/SheetStage.js +11 -0
  158. package/dist/sheets/Step.d.ts +15 -0
  159. package/dist/sheets/Step.js +15 -0
  160. package/dist/type/Icon.d.ts +16 -0
  161. package/dist/type/Icon.js +12 -0
  162. package/dist/type/Text.d.ts +302 -0
  163. package/dist/type/Text.js +16 -0
  164. package/dist/type/icons.d.ts +6 -0
  165. package/dist/type/icons.js +2 -0
  166. package/fonts/LICENSES.md +44 -0
  167. package/fonts/fc-pride-medium.otf +0 -0
  168. package/fonts/pk-nonthaburi-demo.ttf +0 -0
  169. package/icons.svg +61 -0
  170. package/package.json +62 -0
package/css/media.css ADDED
@@ -0,0 +1,232 @@
1
+ /* ============================================================
2
+ cardds/media.css — media & display inside cards:
3
+ cover card · quote · tile grid · fold · waveform
4
+ ============================================================ */
5
+
6
+ /* ---- cover: the image IS the card; text over a scrim ----
7
+ <article class="card card--cover"><div class="ph" role="img"></div>
8
+ <div class="card__head"><span class="t-overline">Featured</span><button class="icon-btn icon-btn--sm">♥</button></div>
9
+ <div><h2 class="t-h1">Art Institute</h2><p class="t-body">111 South Michigan Ave</p></div>
10
+ <div class="row">…meta…</div></article> */
11
+ .card--cover {
12
+ --card-bg: var(--card-3-bg);
13
+ --card-ink: var(--card-3-ink);
14
+ --accent: var(--card-3-ink);
15
+ --on-accent: var(--card-3-bg);
16
+ min-height: 22.5rem;
17
+ align-content: space-between;
18
+ overflow: hidden;
19
+ isolation: isolate;
20
+ }
21
+ .card--cover > img,
22
+ .card--cover > .ph {
23
+ position: absolute;
24
+ inset: 0;
25
+ width: 100%;
26
+ height: 100%;
27
+ object-fit: cover;
28
+ z-index: -2;
29
+ border: 0;
30
+ border-radius: 0;
31
+ min-height: 0;
32
+ }
33
+ /* scrim: dark at both ends so overline and meta stay readable */
34
+ .card--cover::before {
35
+ content: "";
36
+ position: absolute;
37
+ inset: 0;
38
+ z-index: -1;
39
+ background: linear-gradient(to bottom, var(--scrim), transparent 40% 60%, var(--scrim));
40
+ }
41
+ .card--cover .t-muted { color: inherit; opacity: 0.75; }
42
+ .card--cover .row__meta { color: inherit; opacity: 0.85; }
43
+
44
+ /* ---- quote: giant mark, big text, attribution ----
45
+ <blockquote class="quote"><p class="quote__text">It's overcast…</p><footer class="quote__by">Wednesday 15</footer></blockquote> */
46
+ .quote { margin: 0; display: grid; gap: var(--sp-3); }
47
+ .quote::before {
48
+ content: "\201C";
49
+ font: 900 calc(var(--fs-h1) * 3)/0.55 var(--font-display);
50
+ height: var(--sp-10);
51
+ opacity: 0.9;
52
+ }
53
+ .quote__text { margin: 0; font: var(--type-h1); letter-spacing: var(--track-h1); text-wrap: balance; }
54
+ .quote--sm .quote__text { font: var(--type-h2); letter-spacing: var(--track-h2); }
55
+ .quote__by { font: var(--type-overline); letter-spacing: var(--track-overline); text-transform: uppercase; color: var(--card-muted); }
56
+ .card--3 .quote__by { color: inherit; opacity: 0.7; }
57
+
58
+ /* ---- tile grid: big square tiles — icon top, label bottom ----
59
+ <div class="tile-grid"><button class="tile"><svg class="icon"/><span class="tile__label">Light</span></button>
60
+ <label class="tile tile--pick"><input type="checkbox"><svg class="icon"/><span class="tile__label">Music</span></label></div> */
61
+ .tile-grid {
62
+ display: grid;
63
+ grid-template-columns: repeat(auto-fill, minmax(min(var(--tile-min), 100%), 1fr));
64
+ gap: var(--sp-3);
65
+ }
66
+ .tile-grid--3 { --tile-min: 6rem; } /* three across on a phone (attachments, upload kinds) */
67
+ .tile {
68
+ display: grid;
69
+ align-content: space-between;
70
+ justify-items: start;
71
+ gap: var(--sp-5);
72
+ min-height: var(--tile-h);
73
+ min-width: 0;
74
+ padding: var(--sp-4);
75
+ border: var(--border-w) solid var(--outline);
76
+ border-radius: var(--r-card);
77
+ background: var(--action-bg);
78
+ color: var(--action-ink);
79
+ font: inherit;
80
+ text-align: left;
81
+ position: relative;
82
+ cursor: pointer;
83
+ }
84
+ .tile__label { font: var(--type-title); }
85
+ .tile__value { font: var(--type-h2); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
86
+ .tile__sub { font: var(--type-caption); opacity: 0.7; }
87
+ .tile__text { display: grid; gap: var(--sp-0); }
88
+ .tile--on { background: var(--accent); color: var(--on-accent); border-color: transparent; }
89
+ .tile--pick > input { appearance: none; position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
90
+ .tile--pick:has(:checked) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
91
+ .tile--pick:has(:focus-visible) { outline: var(--focus); outline-offset: var(--sp-1); }
92
+ .tile--wide { grid-column: 1 / -1; }
93
+ .tile--stat { min-height: var(--avatar-xl); gap: var(--sp-3); grid-template-columns: auto 1fr; align-items: center; }
94
+ /* upload: dashed, centred plus + label */
95
+ .tile--upload {
96
+ border-style: dashed;
97
+ background: transparent;
98
+ color: inherit;
99
+ align-content: center;
100
+ justify-items: center;
101
+ gap: var(--sp-2);
102
+ text-align: center;
103
+ }
104
+ /* tinted tiles: slot colors inside a card (schedule, notes) */
105
+ .tile--2 { background: var(--card-2-bg); color: var(--card-2-ink); border-color: transparent; }
106
+ .tile--3 { background: var(--card-3-bg); color: var(--card-3-ink); border-color: transparent; }
107
+
108
+ /* ---- fold: a sticky-note corner on any card (decorative) ----
109
+ No border. The card's outline follows its border box, so it ran straight
110
+ across the squared bottom-right corner and cut through the fold's diagonal —
111
+ the corner read as a notch with a line through it rather than a lifted flap.
112
+ The COLOUR goes transparent rather than the border going to 0, so the box
113
+ keeps its geometry and a fold card still measures the same as the cards
114
+ beside it in a deck. */
115
+ .card--fold {
116
+ border-color: transparent;
117
+ border-bottom-right-radius: 0;
118
+ background-image: linear-gradient(to top left,
119
+ var(--surface) 0 50%,
120
+ color-mix(in srgb, currentColor 18%, var(--card-bg)) 50% 100%);
121
+ background-repeat: no-repeat;
122
+ background-position: bottom right;
123
+ background-size: var(--sp-8) var(--sp-8);
124
+ }
125
+
126
+ /* ---- wave: audio waveform stand-in for voice rows ----
127
+ <span class="wave"></span> — sits in a .row__body, fills the width */
128
+ .wave {
129
+ display: block;
130
+ height: var(--icon);
131
+ width: 100%;
132
+ background: repeating-linear-gradient(90deg, currentColor 0 var(--sp-1), transparent var(--sp-1) var(--sp-2));
133
+ /* masks only read alpha: currentColor = "opaque" without naming a color */
134
+ mask: radial-gradient(ellipse 60% 100% at 50% 50%, currentColor 55%, transparent 58%) 0 0 / var(--tap-lg) 100% repeat-x;
135
+ -webkit-mask: radial-gradient(ellipse 60% 100% at 50% 50%, currentColor 55%, transparent 58%) 0 0 / var(--tap-lg) 100% repeat-x;
136
+ opacity: 0.8;
137
+ }
138
+
139
+ /* ---- mosaic: one big photo + two small, inside a card ----
140
+ <div class="mosaic"><div class="ph"></div><div class="ph"></div><div class="ph"></div></div> */
141
+ .mosaic {
142
+ display: grid;
143
+ grid-template-columns: 2fr 1fr;
144
+ grid-auto-rows: 1fr;
145
+ gap: var(--sp-1);
146
+ aspect-ratio: 4 / 3;
147
+ border-radius: var(--r-media);
148
+ overflow: hidden;
149
+ }
150
+ .mosaic > :first-child { grid-row: span 2; }
151
+ .mosaic > * { min-height: 0; border-radius: 0; opacity: 1; border-width: 0; }
152
+ .mosaic > .ph { opacity: 0.4; border-width: var(--border-w); }
153
+ .mosaic > img { width: 100%; height: 100%; object-fit: cover; }
154
+
155
+ /* ---- postcard: a member's introduction, a card in the hand — photo in front,
156
+ words on the back; tap to turn it over (Lh, 2026-09-12; card #63) ----
157
+ <div class="postcard">
158
+ <button class="postcard__face postcard__front" type="button" aria-label="พลิกดูด้านหลัง">
159
+ <img src="…" alt=""> — or <div class="ph"></div> —
160
+ <span class="postcard__label">ป้าแจ๋ว</span>
161
+ </button>
162
+ <button class="postcard__face postcard__back" type="button" aria-label="พลิกดูด้านหน้า">
163
+ <p class="postcard__caption">ชอบชงกาแฟดริปตอนเช้า</p>
164
+ <span class="postcard__label">ป้าแจ๋ว</span>
165
+ </button>
166
+ </div>
167
+ Each face is the whole tap target; cardds.js toggles .is-flipped on the
168
+ postcard (either face, Enter/Space too). A voice note, a like, a reply live
169
+ OUTSIDE the postcard (a Row under it): a face is a button, and a button
170
+ holds no button. The faces share one box (a 3:2 landscape card, the media
171
+ radius); the turn is a Y rotation at --motion-flip, a plain swap under
172
+ prefers-reduced-motion. Reads ink from the card slot it sits in. */
173
+ .postcard {
174
+ position: relative;
175
+ aspect-ratio: 3 / 2;
176
+ perspective: 60rem;
177
+ border-radius: var(--r-media);
178
+ }
179
+ .postcard__face {
180
+ position: absolute;
181
+ inset: 0;
182
+ display: grid;
183
+ align-content: end;
184
+ padding: var(--sp-4);
185
+ gap: var(--sp-2);
186
+ border: var(--border-w) solid var(--card-border);
187
+ border-radius: inherit;
188
+ overflow: hidden;
189
+ isolation: isolate;
190
+ color: inherit;
191
+ text-align: left;
192
+ backface-visibility: hidden;
193
+ -webkit-backface-visibility: hidden;
194
+ transition: transform var(--motion-flip) ease-in-out;
195
+ box-shadow: var(--shadow-float);
196
+ }
197
+ .postcard__face:focus-visible { outline: var(--focus); outline-offset: var(--sp-1); }
198
+ /* the front: the photo (or the wireframe box) fills it, the name reads on a scrim */
199
+ .postcard__front { background: var(--card-3-bg); color: var(--card-3-ink); }
200
+ .postcard__front > img,
201
+ .postcard__front > .ph {
202
+ position: absolute;
203
+ inset: 0;
204
+ width: 100%;
205
+ height: 100%;
206
+ object-fit: cover;
207
+ z-index: -2;
208
+ border: 0;
209
+ border-radius: 0;
210
+ min-height: 0;
211
+ }
212
+ .postcard__front::before {
213
+ content: "";
214
+ position: absolute;
215
+ inset: 0;
216
+ z-index: -1;
217
+ background: linear-gradient(to top, var(--scrim), transparent 45%);
218
+ }
219
+ /* the back: paper — the words, big enough to read at arm's length */
220
+ .postcard__back {
221
+ background: var(--card-2-bg);
222
+ color: var(--card-2-ink);
223
+ align-content: space-between;
224
+ transform: rotateY(180deg);
225
+ }
226
+ .postcard__caption { margin: 0; font: var(--type-title); text-wrap: pretty; }
227
+ .postcard__label { font: var(--type-overline); letter-spacing: var(--track-overline); text-transform: uppercase; opacity: 0.85; }
228
+ .postcard.is-flipped > .postcard__front { transform: rotateY(-180deg); }
229
+ .postcard.is-flipped > .postcard__back { transform: rotateY(0); }
230
+ @media (prefers-reduced-motion: reduce) {
231
+ .postcard__face { transition: none; }
232
+ }
@@ -0,0 +1,258 @@
1
+ /* ============================================================
2
+ cardds/numbers.css — big numbers inside cards:
3
+ ring gauge · step track · data band · dot grid · value picker
4
+ Data goes in as unitless custom properties on the element
5
+ (--value, --done, --total); the math lives here.
6
+ ============================================================ */
7
+
8
+ /* ---- ring: conic gauge, --value 0–100, content centred ----
9
+ <div class="ring" style="--value: 60"><span class="ring__value">60%</span><span class="ring__label">done</span></div> */
10
+ .ring {
11
+ --value: 0;
12
+ --_size: var(--ring);
13
+ --_w: var(--ring-w);
14
+ width: var(--_size);
15
+ height: var(--_size);
16
+ flex: none;
17
+ border-radius: 50%;
18
+ display: grid;
19
+ place-content: center;
20
+ text-align: center;
21
+ position: relative;
22
+ font-variant-numeric: tabular-nums;
23
+ }
24
+ .ring::before {
25
+ content: "";
26
+ position: absolute;
27
+ inset: 0;
28
+ border-radius: 50%;
29
+ background: conic-gradient(var(--accent) calc(var(--value) * 1%), var(--ink-tint) 0);
30
+ /* masks only read alpha: currentColor = "opaque" without naming a color */
31
+ mask: radial-gradient(farthest-side, transparent calc(100% - var(--_w) - 1px), currentColor calc(100% - var(--_w)));
32
+ -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - var(--_w) - 1px), currentColor calc(100% - var(--_w)));
33
+ }
34
+ .ring__value { font: var(--type-stat); letter-spacing: var(--track-display); }
35
+ .ring__label { font: var(--type-caption); color: var(--card-muted); }
36
+ .card--3 .ring__label { color: inherit; opacity: 0.7; }
37
+ .ring--sm { --_size: var(--avatar-xl); --_w: var(--sp-3); }
38
+ .ring--sm .ring__value { font: 800 var(--fs-2xl)/1 var(--font-display); }
39
+ .ring--xs { --_size: var(--tap-sm); --_w: var(--dot-xs); } /* inline counter (step 3 of 5) */
40
+ .ring--xs .ring__value { font: var(--type-overline); }
41
+
42
+ /* ---- track: step progress in a pill — done · done · todo ----
43
+ <ol class="track"><li class="track__step is-done">icon</li> …</ol> */
44
+ .track {
45
+ --_bg: var(--card-3-bg);
46
+ --_ink: var(--card-3-ink);
47
+ /* the track lays down its own dark ground, so like the dark card slot it
48
+ re-points the accent to its own ink (README rule 10). Without this, any
49
+ palette where --accent equals --card-3-bg — field, hydro, and the
50
+ wireframe default — paints done steps invisibly on the track. */
51
+ --accent: var(--_ink);
52
+ --on-accent: var(--_bg);
53
+ list-style: none;
54
+ margin: 0;
55
+ padding: var(--sp-2);
56
+ display: flex;
57
+ align-items: center;
58
+ border-radius: var(--r-chip);
59
+ background: var(--_bg);
60
+ color: var(--_ink);
61
+ }
62
+ .card--3 .track { --_bg: var(--card-3-ink); --_ink: var(--card-3-bg); }
63
+ .track__step {
64
+ flex: 1;
65
+ display: flex;
66
+ align-items: center;
67
+ min-width: 0;
68
+ }
69
+ .track__step:first-child { flex: none; }
70
+ /* the connector runs INTO each step; it lights up when that step is done */
71
+ .track__step::before {
72
+ content: "";
73
+ flex: 1;
74
+ height: var(--sp-3);
75
+ background: color-mix(in srgb, var(--_ink) 30%, transparent);
76
+ }
77
+ .track__step:first-child::before { display: none; }
78
+ .track__step.is-done::before { background: var(--accent); }
79
+ .track__step > .icon,
80
+ .track__step > .track__dot {
81
+ width: var(--tap-lg);
82
+ height: var(--tap-lg);
83
+ flex: none;
84
+ padding: calc((var(--tap-lg) - var(--icon)) / 2);
85
+ border-radius: 50%;
86
+ background: color-mix(in srgb, var(--_ink) 30%, transparent);
87
+ color: var(--_ink);
88
+ }
89
+ .track__step.is-done > .icon,
90
+ .track__step.is-done > .track__dot { background: var(--accent); color: var(--on-accent); }
91
+
92
+ /* ---- band: data card — 2-line label · giant number · footnote ----
93
+ <article class="card card--band"><span class="band__label">Hours<br>banked</span><span class="band__num">18</span><span class="band__foot">this week</span></article> */
94
+ .card--band {
95
+ grid-template-columns: 1fr auto;
96
+ grid-template-areas: "label num" "foot num";
97
+ align-items: end;
98
+ gap: var(--sp-2) var(--sp-4);
99
+ min-height: var(--tile-h);
100
+ }
101
+ .band__label { grid-area: label; align-self: start; font: var(--type-label); font-size: var(--fs-body); line-height: 1.2; }
102
+ .band__num {
103
+ grid-area: num;
104
+ font: 800 var(--fs-giant) / 0.9 var(--font-display);
105
+ letter-spacing: var(--track-display);
106
+ font-variant-numeric: tabular-nums;
107
+ }
108
+ .band__num small { font-size: 0.5em; font-weight: 700; margin-right: 0.1em; } /* unit or % */
109
+ .band__foot { grid-area: foot; font: var(--type-caption); color: var(--card-muted); }
110
+ .card--3 .band__foot { color: inherit; opacity: 0.7; }
111
+ /* band stack: ONE card boundary; bands are frameless inside it, a hairline
112
+ between each. Bands may still carry slot tints (card--2 / card--3). */
113
+ .band-stack {
114
+ display: grid;
115
+ border: var(--border-w) solid var(--card-border);
116
+ border-radius: var(--r-card);
117
+ overflow: hidden;
118
+ }
119
+ .band-stack > .card--band { border: 0; border-radius: 0; box-shadow: none; }
120
+ .band-stack > .card--band + .card--band { border-top: var(--border-w) solid var(--hairline); }
121
+
122
+ /* ---- dotgrid: progress over time as dots — --total, --done, --cols ----
123
+ one element, no children:
124
+ <div class="dotgrid" style="--total: 365; --done: 128" role="img" aria-label="128 of 365 days"></div> */
125
+ .dotgrid {
126
+ --cols: 12;
127
+ --total: 100;
128
+ --done: 0;
129
+ --_rows: round(up, var(--total) / var(--cols));
130
+ --_full: round(down, var(--done) / var(--cols)); /* complete rows */
131
+ --_part: calc(var(--done) - var(--_full) * var(--cols)); /* dots in the partial row */
132
+ --_cell-x: calc(100% / var(--cols));
133
+ --_cell-y: calc(100% / var(--_rows));
134
+ width: 100%;
135
+ aspect-ratio: calc(var(--cols) / var(--_rows));
136
+ position: relative;
137
+ background: radial-gradient(circle, var(--hairline) 0 34%, transparent 37%) 0 0 / var(--_cell-x) var(--_cell-y);
138
+ }
139
+ .dotgrid::before {
140
+ content: "";
141
+ position: absolute;
142
+ inset: 0;
143
+ background: radial-gradient(circle, var(--accent) 0 34%, transparent 37%) 0 0 / var(--_cell-x) var(--_cell-y);
144
+ clip-path: polygon(
145
+ 0 0,
146
+ 100% 0,
147
+ 100% calc(var(--_full) * var(--_cell-y)),
148
+ calc(var(--_part) * var(--_cell-x)) calc(var(--_full) * var(--_cell-y)),
149
+ calc(var(--_part) * var(--_cell-x)) calc((var(--_full) + 1) * var(--_cell-y)),
150
+ 0 calc((var(--_full) + 1) * var(--_cell-y))
151
+ );
152
+ }
153
+ /* no round(): show the grid without the done fill rather than a broken shape */
154
+ @supports not (width: round(up, 1px, 1px)) {
155
+ .dotgrid::before { display: none; }
156
+ }
157
+ .dotgrid--stars {
158
+ background-image: none;
159
+ }
160
+ .dotgrid--stars,
161
+ .dotgrid--stars::before {
162
+ --_star: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
163
+ }
164
+
165
+ /* ---- picker: horizontal snap picker, centre value big ----
166
+ <div class="picker"><div class="picker__rail"><button class="picker__item">100</button>…</div><span class="picker__unit">ml</span></div> */
167
+ .picker { display: grid; gap: var(--sp-1); justify-items: center; position: relative; }
168
+ .picker__rail {
169
+ width: 100%;
170
+ display: grid;
171
+ grid-auto-flow: column;
172
+ /* side padding = one third of the rail, so the first and last value can sit
173
+ in the centre; the content box is then one third wide, and each column
174
+ fills it (percent columns resolve against the content box) */
175
+ padding-inline: calc(100% / 3);
176
+ grid-auto-columns: 100%;
177
+ overflow-x: auto;
178
+ scroll-snap-type: x mandatory;
179
+ scrollbar-width: none;
180
+ position: relative;
181
+ }
182
+ .picker__rail::-webkit-scrollbar { display: none; }
183
+ .picker__item {
184
+ scroll-snap-align: center;
185
+ border: 0;
186
+ background: transparent;
187
+ color: inherit;
188
+ padding: var(--sp-2) 0;
189
+ font: var(--type-stat);
190
+ letter-spacing: var(--track-display);
191
+ font-variant-numeric: tabular-nums;
192
+ opacity: 0.3;
193
+ text-align: center;
194
+ }
195
+ .picker__item[aria-selected="true"] { opacity: 1; }
196
+ /* centre rules either side of the selected value */
197
+ .picker::before,
198
+ .picker::after {
199
+ content: "";
200
+ position: absolute;
201
+ top: var(--sp-2);
202
+ height: calc(var(--sp-10) + var(--sp-3));
203
+ width: var(--rule-w);
204
+ background: var(--accent);
205
+ left: calc(100% / 3);
206
+ }
207
+ .picker::after { left: auto; right: calc(100% / 3); }
208
+ .picker__unit { font: var(--type-caption); color: var(--card-muted); }
209
+ .card--3 .picker__unit { color: inherit; opacity: 0.7; }
210
+ /* the scrolled-to item lights up by position — no JS */
211
+ @supports (animation-timeline: view(x)) {
212
+ .picker__item { opacity: 0.3; animation: picker-focus linear both; animation-timeline: view(x); }
213
+ @keyframes picker-focus {
214
+ 0%, 100% { opacity: 0.3; }
215
+ 45%, 55% { opacity: 1; }
216
+ }
217
+ }
218
+
219
+ /* ---- badge: tiny count next to a title or tab ("Leads +9", "All 23") ----
220
+ <h3 class="t-h2">Leads <span class="badge">+9</span></h3> */
221
+ .badge {
222
+ display: inline-grid;
223
+ place-items: center;
224
+ min-width: var(--badge);
225
+ height: var(--badge);
226
+ padding: 0 var(--sp-2);
227
+ border-radius: var(--r-chip);
228
+ background: var(--accent);
229
+ color: var(--on-accent);
230
+ font: var(--type-label);
231
+ font-variant-numeric: tabular-nums;
232
+ vertical-align: middle;
233
+ }
234
+ .badge--soft { background: var(--ink-tint); color: inherit; }
235
+
236
+ /* ---- bars: three or four bars with a count on top — the glance chart ----
237
+ <div class="bars"><div class="bar" style="--v: 16"><span>+16</span></div>… --max on the parent */
238
+ .bars { --max: 20; --_h: 5rem; display: flex; align-items: end; gap: var(--sp-2); height: calc(var(--_h) + var(--sp-8)); }
239
+ .bar {
240
+ flex: 1;
241
+ min-width: 0;
242
+ display: grid;
243
+ align-content: end;
244
+ justify-items: center;
245
+ gap: var(--sp-1);
246
+ height: 100%;
247
+ font: var(--type-caption);
248
+ font-weight: 700;
249
+ }
250
+ .bar::after {
251
+ content: "";
252
+ width: 100%;
253
+ height: max(var(--sp-2), calc(var(--v) / var(--max) * var(--_h)));
254
+ border-radius: var(--r-badge);
255
+ background: var(--ink-tint);
256
+ }
257
+ .bar--on::after { background: var(--accent); }
258
+ .bar--on > span { background: var(--accent); color: var(--on-accent); padding: var(--sp-0) var(--sp-2); border-radius: var(--r-chip); }
@@ -0,0 +1,112 @@
1
+ /* ============================================================
2
+ cardds/palette.css — COLOR SETTINGS. The only file you tune.
3
+ Apply with <html data-palette="clay"> (or <body>).
4
+ No attribute = wireframe grayscale from tokens.css.
5
+ Each palette overrides role tokens only — components never change.
6
+ ============================================================ */
7
+
8
+ /* ---- CLAY — earth-toned: cream surface, terracotta / mustard / ink cards ---- */
9
+ [data-palette="clay"] {
10
+ --surface: #ece5d8;
11
+ --on-surface: #191713;
12
+ --on-surface-muted: #6f675a;
13
+
14
+ --card-1-bg: #e8b647; --card-1-ink: #191713; /* mustard hero */
15
+ --card-2-bg: #e05c3a; --card-2-ink: #191713; /* terracotta */
16
+ --card-3-bg: #1c1a17; --card-3-ink: #f4efe4; /* ink */
17
+
18
+ --card-border: transparent;
19
+ --card-muted: #45412f; /* 5.5:1 on the mustard card — AA for captions and metas (#55503f was 4.3:1) */
20
+ --card-2-muted: var(--card-2-ink); /* terracotta holds 5:1 for its ink alone; nothing lighter passes AA, so tone 2 has no muted */
21
+
22
+ --accent: #1c1a17; --on-accent: #f4efe4;
23
+ --action-bg: #f7f3e9; --action-ink: #191713; --action-border: transparent;
24
+
25
+ --pos: #3e7d4f; --neg: #c23c1d;
26
+ --dot-a: #e05c3a; --dot-b: #6f6be0;
27
+ }
28
+
29
+ /* ---- SHINE — shiny: periwinkle surface, coral / black / gold cards ---- */
30
+ [data-palette="shine"] {
31
+ --surface: #6d6ae4;
32
+ --on-surface: #ffffff;
33
+ --on-surface-muted: #d3d2f7;
34
+
35
+ --card-1-bg: #eec85a; --card-1-ink: #16161a;
36
+ --card-2-bg: #e8624a; --card-2-ink: #16161a;
37
+ --card-3-bg: #1b1b1f; --card-3-ink: #ffffff;
38
+
39
+ --card-border: transparent;
40
+ --card-muted: #4c4a44;
41
+
42
+ --accent: #1b1b1f; --on-accent: #ffffff;
43
+ --action-bg: #ffffff; --action-ink: #16161a; --action-border: transparent;
44
+
45
+ --pos: #2e9e5b; --neg: #d6402a;
46
+ --dot-a: #e8624a; --dot-b: #6d6ae4;
47
+ }
48
+
49
+ /* ---- FIELD — warm peach editorial: cream surface, orange accent ---- */
50
+ [data-palette="field"] {
51
+ --surface: #f9e8dc;
52
+ --on-surface: #241f1b;
53
+ --on-surface-muted: #8a7d72;
54
+
55
+ --card-1-bg: #ffffff; --card-1-ink: #241f1b;
56
+ --card-2-bg: #f6d9c5; --card-2-ink: #241f1b;
57
+ --card-3-bg: #ef4e0b; --card-3-ink: #fff4ec;
58
+
59
+ --card-border: transparent;
60
+ --card-muted: #8a7d72;
61
+
62
+ --accent: #ef4e0b; --on-accent: #fff4ec;
63
+ --action-bg: #ffffff; --action-ink: #241f1b; --action-border: transparent;
64
+
65
+ --pos: #2e8f52; --neg: #d13b1a;
66
+ --dot-a: #ef4e0b; --dot-b: #241f1b;
67
+ }
68
+
69
+ /* ---- ROAST — coffee: warm cream surface, caramel accent ---- */
70
+ [data-palette="roast"] {
71
+ --surface: #f6f1e7;
72
+ --on-surface: #2b211a;
73
+ --on-surface-muted: #8c7f70;
74
+
75
+ --card-1-bg: #ffffff; --card-1-ink: #2b211a;
76
+ --card-2-bg: #e9dcc6; --card-2-ink: #2b211a;
77
+ --card-3-bg: #b97e3c; --card-3-ink: #fdf9f1;
78
+
79
+ --card-border: transparent;
80
+ --card-muted: #8c7f70;
81
+
82
+ --accent: #b97e3c; --on-accent: #fdf9f1;
83
+ --action-bg: #ffffff; --action-ink: #2b211a; --action-border: transparent;
84
+
85
+ --pos: #3e7d4f; --neg: #bf4028;
86
+ --dot-a: #b97e3c; --dot-b: #2b211a;
87
+ }
88
+
89
+ /* ---- HYDRO — cool shiny blue: airy surface, saturated blue CTA ---- */
90
+ [data-palette="hydro"] {
91
+ --surface: #e9f0fb;
92
+ --on-surface: #17203a;
93
+ --on-surface-muted: #64729a;
94
+
95
+ --card-1-bg: #ffffff; --card-1-ink: #17203a;
96
+ --card-2-bg: #cddcf7; --card-2-ink: #17203a;
97
+ --card-3-bg: #3d63e0; --card-3-ink: #f2f6ff;
98
+
99
+ --card-border: transparent;
100
+ --card-muted: #64729a;
101
+
102
+ --accent: #3d63e0; --on-accent: #f2f6ff;
103
+ --action-bg: #ffffff; --action-ink: #17203a; --action-border: transparent;
104
+
105
+ --pos: #2e8f52; --neg: #d13b1a;
106
+ --dot-a: #3d63e0; --dot-b: #eec85a;
107
+ }
108
+
109
+ /* Colored palettes trade wireframe outlines for soft elevation. */
110
+ [data-palette] :is(.card, .band-stack) { box-shadow: var(--shadow-card); }
111
+ /* …but controls keep a faint edge so they still read as controls */
112
+ [data-palette] { --outline: color-mix(in srgb, currentColor 25%, transparent); }
package/css/people.css ADDED
@@ -0,0 +1,79 @@
1
+ /* ============================================================
2
+ cardds/people.css — avatars: single, stacked, chooser row.
3
+ A person row = .row with an .avatar as lead (see lists.css).
4
+ ============================================================ */
5
+
6
+ .avatar--center { margin-inline: auto; } /* centred in a sheet head */
7
+
8
+ /* ---- avatar: initials by default, <img> when there is a photo ----
9
+ <span class="avatar">LH</span> · <span class="avatar"><img src alt></span> */
10
+ .avatar {
11
+ --_size: var(--avatar);
12
+ width: var(--_size);
13
+ height: var(--_size);
14
+ flex: none;
15
+ border-radius: 50%;
16
+ display: grid;
17
+ place-items: center;
18
+ background: var(--card-2-bg);
19
+ color: var(--card-2-ink);
20
+ font: var(--type-label);
21
+ font-size: calc(var(--_size) * 0.3);
22
+ text-transform: uppercase;
23
+ letter-spacing: 0.02em;
24
+ position: relative;
25
+ text-decoration: none; /* .avatar works as <a> too */
26
+ }
27
+ .avatar > img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
28
+ .avatar > .icon { width: 50%; height: 50%; }
29
+ .avatar--sm { --_size: var(--avatar-sm); }
30
+ .avatar--lg { --_size: var(--avatar-lg); }
31
+ .avatar--xl { --_size: var(--avatar-xl); } /* sheet head icon ring */
32
+ /* outline avatar: no fill, an edge — icons in a ring, "add" slots */
33
+ .avatar--outline { background: transparent; color: inherit; border: var(--border-w) solid var(--outline); }
34
+ .avatar--add { background: transparent; color: inherit; border: var(--border-w) dashed var(--outline); cursor: pointer; }
35
+ /* accent avatar: the "+7" tail, the selected person */
36
+ .avatar--on { background: var(--accent); color: var(--on-accent); }
37
+ /* halo: faint concentric rings behind a hero avatar (decorative) */
38
+ .avatar--halo::before {
39
+ content: "";
40
+ position: absolute;
41
+ inset: calc(-0.55 * var(--_size));
42
+ border-radius: 50%;
43
+ background: repeating-radial-gradient(circle, transparent 0 calc(var(--_size) * 0.5), currentColor calc(var(--_size) * 0.5) calc(var(--_size) * 0.5 + var(--border-w)), transparent calc(var(--_size) * 0.5 + var(--border-w)) calc(var(--_size) * 0.68));
44
+ opacity: 0.25;
45
+ pointer-events: none;
46
+ }
47
+
48
+ /* ---- avatar stack: overlapping heads, optional +N tail ----
49
+ <span class="avatar-stack"><span class="avatar avatar--sm">A</span>…<span class="avatar avatar--sm avatar--on">+7</span></span> */
50
+ .avatar-stack { display: inline-flex; align-items: center; }
51
+ .avatar-stack > .avatar { box-shadow: 0 0 0 var(--sp-1) var(--card-bg, var(--surface)); }
52
+ .avatar-stack > .avatar + .avatar { margin-left: calc(-1 * var(--sp-3)); }
53
+
54
+ /* ---- avatar chooser: big heads with name + role under, scrolls sideways ----
55
+ <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> */
56
+ .avatar-pick {
57
+ display: flex;
58
+ gap: var(--sp-4);
59
+ overflow-x: auto;
60
+ scroll-snap-type: x proximity;
61
+ scrollbar-width: none; /* starts at the card's padding edge, like every row */
62
+ }
63
+ .avatar-pick::-webkit-scrollbar { display: none; }
64
+ .avatar-pick__item {
65
+ scroll-snap-align: start;
66
+ flex: none;
67
+ width: var(--avatar-lg);
68
+ display: grid;
69
+ justify-items: center;
70
+ gap: var(--sp-2);
71
+ border: 0;
72
+ background: transparent;
73
+ color: inherit;
74
+ padding: 0;
75
+ text-align: center;
76
+ }
77
+ .avatar-pick__name { font: var(--type-label); font-size: var(--fs-sm); }
78
+ .avatar-pick__role { font: var(--type-caption); color: var(--card-muted); margin-top: calc(-1 * var(--sp-1)); }
79
+ .card--3 .avatar-pick__role { color: inherit; opacity: 0.7; }