@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/forms.css ADDED
@@ -0,0 +1,64 @@
1
+ /* ============================================================
2
+ cardds/forms.css — inputs + inline form actions
3
+ .field: flat outlined input, the label IS the placeholder.
4
+ Markup: <label class="field"><svg?/><input placeholder="Passport Number"></label>
5
+ ============================================================ */
6
+
7
+ .field {
8
+ display: flex;
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);
17
+ min-width: 0;
18
+ }
19
+
20
+ .field input,
21
+ .field select {
22
+ border: 0;
23
+ background: transparent;
24
+ color: inherit;
25
+ font: var(--type-body);
26
+ width: 100%;
27
+ min-width: 0;
28
+ outline-offset: var(--sp-1);
29
+ }
30
+ .field input::placeholder { color: inherit; opacity: 0.55; }
31
+
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); }
34
+ .field--action > .icon-btn { margin-left: auto; }
35
+
36
+ /* colored palettes drop the visible outline elsewhere — fields keep one */
37
+ [data-palette] .field { border-color: color-mix(in srgb, currentColor 25%, transparent); }
38
+
39
+ /* ---- add-row: inline "+ add another" action ---- */
40
+ .add-row {
41
+ display: inline-flex;
42
+ align-items: center;
43
+ gap: var(--sp-2);
44
+ border: 0;
45
+ background: none;
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);
51
+ }
52
+ .add-row__plus {
53
+ width: var(--icon);
54
+ height: var(--icon);
55
+ flex: none;
56
+ border-radius: 50%;
57
+ background: var(--accent);
58
+ color: var(--on-accent);
59
+ display: grid;
60
+ place-items: center;
61
+ font-size: var(--fs-md);
62
+ line-height: 1;
63
+ }
64
+ .add-row__plus .icon { width: var(--icon-xs); height: var(--icon-xs); }
@@ -0,0 +1,61 @@
1
+ /* ============================================================
2
+ cardds/journey.css — trip/status primitives inside cards:
3
+ route row, tile badge, note row. Generic: flights, buses,
4
+ deliveries — anything with endpoints and a status.
5
+ ============================================================ */
6
+
7
+ /* ---- route: endpoint · dotted connector (icon in middle) · endpoint ---- */
8
+ .route { display: flex; align-items: center; gap: var(--sp-3); }
9
+
10
+ .route__point { display: grid; gap: var(--sp-0); min-width: 0; }
11
+ .route__point--end { text-align: right; }
12
+
13
+ .route__code { font: var(--type-h2); letter-spacing: var(--track-h2); }
14
+ .route__time { font: var(--type-body); font-variant-numeric: tabular-nums; }
15
+ .route__place { font: var(--type-caption); color: var(--card-muted); }
16
+ .card--3 .route__place { color: inherit; opacity: 0.7; }
17
+
18
+ .route__via {
19
+ flex: 1;
20
+ display: flex;
21
+ align-items: center;
22
+ gap: var(--sp-1);
23
+ opacity: 0.65;
24
+ font-size: var(--fs-body);
25
+ }
26
+ .route__via::before,
27
+ .route__via::after {
28
+ content: "";
29
+ flex: 1;
30
+ border-top: var(--rule-w) dotted currentColor;
31
+ }
32
+
33
+ /* ---- tile badge: tiny label over a big value (gate, seat, zone) ---- */
34
+ .tile-badge {
35
+ display: inline-grid;
36
+ justify-items: center;
37
+ gap: var(--sp-0);
38
+ padding: var(--sp-1) var(--sp-2);
39
+ border-radius: var(--r-badge);
40
+ background: var(--card-3-bg);
41
+ color: var(--card-3-ink);
42
+ }
43
+ .tile-badge__label { font: var(--type-overline); letter-spacing: var(--track-overline); text-transform: uppercase; opacity: 0.7; }
44
+ .tile-badge__value { font: 800 var(--fs-lg)/1.1 var(--font-display); font-variant-numeric: tabular-nums; }
45
+
46
+ /* plain: no fill — a date box beside a title (Jun / 18) */
47
+ .tile-badge--plain { background: transparent; color: inherit; padding: 0; min-width: var(--tap-sm); }
48
+ .tile-badge--plain .tile-badge__value { font: 800 var(--fs-h1)/1 var(--font-display); }
49
+
50
+ /* corner badge: pin a tile badge on media (rating, price) — .card__media is relative */
51
+ .tile-badge--corner { position: absolute; right: var(--sp-3); bottom: var(--sp-3); }
52
+
53
+ /* ---- note row: icon + caption (transit hints, fine print) ---- */
54
+ .note-row {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: var(--sp-2);
58
+ font: var(--type-caption);
59
+ color: var(--card-muted);
60
+ }
61
+ .card--3 .note-row { color: inherit; opacity: 0.7; }
@@ -0,0 +1,243 @@
1
+ /* ============================================================
2
+ cardds/layover.css — cards that lay OVER other content:
3
+ ask drawer · popover menu · expanding banner · float bar ·
4
+ callout · swipe deck. The sheet (peek / half / full / page)
5
+ lives in sheet.css; these compose with .sheet-stage.
6
+ ============================================================ */
7
+
8
+ /* ---- ask drawer: the content card lifts, a dark drawer asks ----
9
+ <div class="sheet-stage sheet-stage--ask">
10
+ <article class="card lift">…page content…</article>
11
+ <div class="drawer"><h2 class="t-h2">Take the case?</h2><p class="t-caption">This cannot be undone.</p>
12
+ <div class="btn-trio"><button class="icon-btn">×</button><span class="btn-trio__dots"></span><button class="icon-btn icon-btn--invert">?</button><span class="btn-trio__dots"></span><button class="icon-btn">✓</button></div>
13
+ </div></div> */
14
+ .sheet-stage--ask {
15
+ --drawer-h: 14rem;
16
+ background: var(--card-3-bg);
17
+ color: var(--card-3-ink);
18
+ }
19
+ .lift {
20
+ position: absolute;
21
+ inset: 0 0 var(--drawer-h) 0;
22
+ border: 0;
23
+ border-radius: 0 0 var(--r-card) var(--r-card);
24
+ overflow: hidden;
25
+ overscroll-behavior: contain;
26
+ box-shadow: var(--shadow-sheet);
27
+ align-content: start;
28
+ }
29
+ .drawer {
30
+ position: absolute;
31
+ inset-inline: 0;
32
+ bottom: 0;
33
+ height: var(--drawer-h);
34
+ padding: var(--sp-5);
35
+ display: grid;
36
+ align-content: center;
37
+ justify-items: center;
38
+ gap: var(--sp-1);
39
+ text-align: center;
40
+ --accent: var(--card-3-ink); /* the one solid button inverts on the dark drawer */
41
+ --on-accent: var(--card-3-bg);
42
+ }
43
+ .drawer .t-caption { opacity: 0.7; }
44
+ .drawer .icon-btn { background: transparent; color: inherit; border-color: var(--ink-rail); }
45
+ .drawer .icon-btn--invert { background: var(--accent); color: var(--on-accent); border-color: transparent; width: var(--tap-lg); height: var(--tap-lg); }
46
+ .btn-trio { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-3); }
47
+ .btn-trio__dots {
48
+ width: var(--sp-5);
49
+ height: var(--sp-1);
50
+ 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;
51
+ opacity: 0.4;
52
+ }
53
+
54
+ /* ---- popover menu: a card anchored to its button, rows of icon + label ----
55
+ <button class="chip" popovertarget="when">Now ▾</button>
56
+ <div class="menu" id="when" popover><button class="menu__item"><svg class="icon"/>Now</button>…</div>
57
+ Native popover: light dismiss, Esc, top layer. Anchored to the invoker where
58
+ anchor positioning exists; the fallback is the browser's centred placement. */
59
+ .menu {
60
+ display: none; /* explicit closed state — never display on the base rule */
61
+ margin: 0;
62
+ inset: auto;
63
+ border: var(--border-w) solid var(--action-border);
64
+ border-radius: var(--r-card);
65
+ padding: var(--sp-2);
66
+ min-width: 12rem;
67
+ background: var(--action-bg);
68
+ color: var(--action-ink);
69
+ box-shadow: var(--shadow-float);
70
+ }
71
+ .menu:popover-open { display: grid; }
72
+ .menu:not([popover]) { display: grid; position: relative; } /* inline usage (no attribute) */
73
+ .menu__item {
74
+ display: flex;
75
+ align-items: center;
76
+ gap: var(--sp-3);
77
+ min-height: var(--tap);
78
+ width: 100%;
79
+ padding: 0 var(--sp-3);
80
+ border: 0;
81
+ border-radius: var(--r-control);
82
+ background: transparent;
83
+ color: inherit;
84
+ font: var(--type-title);
85
+ text-align: left;
86
+ cursor: pointer;
87
+ }
88
+ .menu__item:hover, .menu__item:focus-visible { background: var(--ink-tint); outline: none; }
89
+ .menu__item[aria-current], .menu__item.is-on { background: var(--accent); color: var(--on-accent); }
90
+ @supports (anchor-name: --a) {
91
+ .menu[popover] {
92
+ position-area: block-end span-inline-end;
93
+ margin-top: var(--sp-2);
94
+ min-width: max(12rem, anchor-size(width)); /* at least as wide as its trigger */
95
+ position-try-fallbacks: flip-block, flip-inline;
96
+ }
97
+ }
98
+ @media (prefers-reduced-motion: no-preference) {
99
+ .menu[popover] {
100
+ opacity: 0;
101
+ translate: 0 calc(-1 * var(--sp-2));
102
+ transition: display 0.2s allow-discrete, overlay 0.2s allow-discrete, opacity 0.2s, translate 0.2s;
103
+ }
104
+ .menu:popover-open { opacity: 1; translate: 0 0; }
105
+ @starting-style { .menu:popover-open { opacity: 0; translate: 0 calc(-1 * var(--sp-2)); } }
106
+ }
107
+
108
+ /* ---- banner: a chip that opens into a tinted card, no JS ----
109
+ <details class="banner"><summary class="chip chip--on">Save $30 on orders over $70 <svg class="icon icon--xs"/></summary>
110
+ <div class="card card--3 banner__body">…</div></details> */
111
+ .banner { display: grid; gap: var(--sp-2); justify-items: start; }
112
+ .banner > summary { list-style: none; cursor: pointer; }
113
+ .banner > summary::-webkit-details-marker { display: none; }
114
+ .banner > summary .icon { transition: rotate 0.2s ease; }
115
+ .banner[open] > summary .icon { rotate: 180deg; }
116
+ .banner__body { width: 100%; padding: var(--sp-4) var(--sp-5); --card-gap: var(--sp-1); }
117
+ .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 */
118
+ @supports (interpolate-size: allow-keywords) {
119
+ @media (prefers-reduced-motion: no-preference) {
120
+ .banner { interpolate-size: allow-keywords; }
121
+ .banner::details-content { block-size: 0; overflow: clip; transition: block-size 0.25s ease, content-visibility 0.25s allow-discrete; }
122
+ .banner[open]::details-content { block-size: auto; }
123
+ }
124
+ }
125
+
126
+ /* ---- float bar: glass pill nav + detached round button, floats over content ----
127
+ <nav class="float-bar"><div class="float-bar__pill"><a class="float-bar__item" aria-current="page"><svg class="icon"/>Left</a>…</div><button class="icon-btn">…</button></nav> */
128
+ .float-bar {
129
+ position: fixed;
130
+ inset-inline: 0;
131
+ bottom: var(--sp-5);
132
+ max-width: var(--screen-w);
133
+ margin-inline: auto;
134
+ padding-inline: var(--sp-4);
135
+ display: flex;
136
+ gap: var(--sp-2);
137
+ align-items: center;
138
+ z-index: 30;
139
+ }
140
+ .sheet-stage > .float-bar { position: absolute; padding-inline: var(--sp-4); }
141
+ /* .screen's default bottom padding clears an .action-bar; a .float-bar is
142
+ taller (--bar-h + its own bottom offset), so reserve the difference. Any
143
+ parent that hosts the bar counts (see .action-bar's reserve, actions.css) */
144
+ :has(> .float-bar) .screen:not(.screen--fill) { padding-bottom: calc(var(--bar-h) + var(--sp-5) + var(--sp-6)); }
145
+ .float-bar__pill {
146
+ flex: 1;
147
+ min-width: 0;
148
+ display: flex;
149
+ justify-content: space-around;
150
+ align-items: center;
151
+ min-height: var(--bar-h);
152
+ padding: var(--sp-1);
153
+ border-radius: var(--r-chip);
154
+ border: var(--border-w) solid var(--action-border);
155
+ background: color-mix(in srgb, var(--action-bg) 82%, transparent);
156
+ color: var(--action-ink);
157
+ backdrop-filter: blur(var(--sp-3));
158
+ -webkit-backdrop-filter: blur(var(--sp-3));
159
+ box-shadow: var(--shadow-float);
160
+ }
161
+ .float-bar__item {
162
+ flex: 1;
163
+ min-width: 0;
164
+ display: grid;
165
+ justify-items: center;
166
+ gap: var(--sp-0);
167
+ min-height: var(--tap-lg);
168
+ padding: var(--sp-1);
169
+ border: 0;
170
+ border-radius: var(--r-chip);
171
+ background: transparent;
172
+ color: inherit;
173
+ font: var(--type-caption);
174
+ text-decoration: none;
175
+ opacity: 0.6;
176
+ overflow: hidden;
177
+ white-space: nowrap;
178
+ text-overflow: ellipsis;
179
+ }
180
+ /* aria-current="false" is the correct way to mark the OTHER tabs, so the
181
+ bare attribute selector lights every one of them up */
182
+ .float-bar__item[aria-current]:not([aria-current="false"]),
183
+ .float-bar__item.is-on { background: var(--accent); color: var(--on-accent); opacity: 1; }
184
+ .float-bar > .icon-btn { width: var(--bar-h); height: var(--bar-h); box-shadow: var(--shadow-float); }
185
+
186
+ /* ---- callout: a small card with a tail — over a map, a media, a mascot ----
187
+ <div class="callout" style="top: 30%; left: 40%"><span class="callout__title">Telok Ayer Park</span><span class="callout__sub">1.2 km</span></div> */
188
+ .callout {
189
+ position: absolute;
190
+ display: grid;
191
+ gap: var(--sp-0);
192
+ width: max-content;
193
+ max-width: 15rem;
194
+ padding: var(--sp-3) var(--sp-4);
195
+ border: var(--border-w) solid var(--action-border);
196
+ border-radius: var(--r-card);
197
+ background: var(--action-bg);
198
+ color: var(--action-ink);
199
+ box-shadow: var(--shadow-float);
200
+ text-align: center;
201
+ }
202
+ .callout::after {
203
+ content: "";
204
+ position: absolute;
205
+ left: 50%;
206
+ bottom: calc(-1 * var(--sp-2));
207
+ width: var(--sp-3);
208
+ height: var(--sp-3);
209
+ translate: -50% 0;
210
+ rotate: 45deg;
211
+ background: inherit;
212
+ border: inherit;
213
+ border-top: 0;
214
+ border-left: 0;
215
+ }
216
+ .callout__title { font: var(--type-title); }
217
+ .callout__sub { font: var(--type-caption); opacity: 0.7; }
218
+ /* speech chip: a chip with the same tail (mascot / greeting) */
219
+ .chip--bubble { position: relative; }
220
+ .chip--bubble::after {
221
+ content: "";
222
+ position: absolute;
223
+ left: var(--sp-4);
224
+ bottom: calc(-1 * var(--sp-2) + var(--border-w));
225
+ width: var(--sp-3);
226
+ height: var(--sp-3);
227
+ rotate: 45deg;
228
+ background: inherit;
229
+ border: inherit;
230
+ border-top: 0;
231
+ border-left: 0;
232
+ }
233
+
234
+ /* ---- deck: swipe cards; the last child is on top ----
235
+ <div class="deck"><article class="card">…</article><article class="card">…</article><article class="card card--fold">front</article></div>
236
+ <div class="deck__actions"><button class="icon-btn">×</button><button class="icon-btn icon-btn--invert">✓</button></div> */
237
+ .deck { display: grid; place-items: center; padding: var(--sp-4) var(--sp-4) var(--sp-6); }
238
+ .deck > .card { grid-area: 1 / 1; width: min(100%, 20rem); min-height: 12.5rem; transition: rotate 0.3s ease, translate 0.3s ease; }
239
+ .deck > .card:nth-last-child(2) { rotate: -4deg; translate: calc(-1 * var(--sp-2)) var(--sp-2); opacity: 0.85; }
240
+ .deck > .card:nth-last-child(3) { rotate: 5deg; translate: var(--sp-3) var(--sp-4); opacity: 0.6; }
241
+ .deck > .card:nth-last-child(n + 4) { display: none; }
242
+ .deck__actions { display: flex; justify-content: center; gap: var(--sp-8); }
243
+ .deck__actions > .icon-btn { width: var(--bar-h); height: var(--bar-h); }
package/css/lists.css ADDED
@@ -0,0 +1,142 @@
1
+ /* ============================================================
2
+ cardds/lists.css — frameless rows and pairs INSIDE a card:
3
+ row (lead · body · end) · key-value rows · kv grid ·
4
+ timeline · legend · link
5
+ A row inside a card has no boundary of its own; .rows draws
6
+ hairlines between rows. A row that IS a card = .card--row.
7
+ ============================================================ */
8
+
9
+ /* ---- row: leading visual · title + meta · trailing control ----
10
+ <div class="row"><span class="avatar">A</span><div class="row__body"><span class="row__title">Alice Kau</span><span class="row__meta">Owner</span></div><button class="icon-btn icon-btn--sm">›</button></div> */
11
+ .row {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: var(--sp-3);
15
+ min-height: var(--tap);
16
+ padding-block: var(--sp-2);
17
+ min-width: 0;
18
+ color: inherit;
19
+ text-decoration: none; /* .row works as <a> too — it is a tap target */
20
+ }
21
+ .row__lead {
22
+ width: var(--tap);
23
+ height: var(--tap);
24
+ flex: none;
25
+ border-radius: var(--r-control);
26
+ border: var(--border-w) solid var(--outline);
27
+ display: grid;
28
+ place-items: center;
29
+ }
30
+ .row__body { flex: 1; min-width: 0; display: grid; gap: var(--sp-0); }
31
+ .row__title { font: var(--type-title); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-block: 0.12em; margin-block: -0.12em; } /* the clip keeps Thai marks — see card.css */
32
+ .row__meta { font: var(--type-caption); color: var(--card-muted); display: flex; align-items: center; gap: var(--sp-1); }
33
+ .card--3 .row__meta { color: inherit; opacity: 0.7; }
34
+ .row__end { margin-left: auto; text-align: right; display: grid; gap: var(--sp-0); justify-items: end; flex: none; }
35
+ .row--lg { min-height: var(--fab); padding-block: var(--sp-3); } /* menu rows: more air, round arrow at the end */
36
+ .row--lg .row__title { font: 700 var(--fs-lg)/1.2 var(--font-display); }
37
+ .row--flush { padding-block: 0; } /* a single row as card content: the card's gap is the spacing */
38
+ /* a finished item in a checklist (a to-do list, an onboarding "complete your introduction" list):
39
+ the words go muted, a filled tick sits at the end, and the row is no longer a tap target —
40
+ pointer-events off here, aria-disabled from the markup. The tick is the .check's checked look
41
+ at --tap-sm, but not an input: nothing to press. */
42
+ .row--done { pointer-events: none; cursor: default; }
43
+ .row--done .row__title { color: var(--card-muted); }
44
+ .card--3 .row--done .row__title { color: inherit; opacity: 0.7; }
45
+ .row__done {
46
+ width: var(--tap-sm);
47
+ height: var(--tap-sm);
48
+ flex: none;
49
+ margin-left: auto;
50
+ border-radius: 50%;
51
+ background: var(--accent);
52
+ color: var(--on-accent);
53
+ display: grid;
54
+ place-items: center;
55
+ }
56
+
57
+ /* rows: a list of rows separated by hairlines */
58
+ .rows { display: grid; }
59
+ .rows > .row + .row { border-top: var(--border-w) solid var(--hairline); }
60
+
61
+ /* ---- kv: label left, value right ----
62
+ <dl class="kv"><div class="kv__row"><dt class="kv__key">Pay with</dt><dd class="kv__val">GHO</dd></div>…</dl> */
63
+ .kv { margin: 0; display: grid; }
64
+ .kv__row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); min-height: var(--tap); }
65
+ .kv--lined > .kv__row + .kv__row { border-top: var(--border-w) solid var(--hairline); }
66
+ .kv__key { font: var(--type-body); color: var(--card-muted); }
67
+ .card--3 .kv__key { color: inherit; opacity: 0.7; }
68
+ .kv__val { margin: 0; font: var(--type-title); display: inline-flex; align-items: center; gap: var(--sp-2); text-align: right; min-width: 0; }
69
+
70
+ /* ---- kv grid: value over label, cells split by a rule (Sex | Weight) ----
71
+ <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> */
72
+ .kv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(var(--tile-min), 100%), 1fr)); gap: var(--sp-4); }
73
+ .kv-grid > .kv-cell + .kv-cell { border-left: var(--border-w) solid var(--hairline); padding-left: var(--sp-4); }
74
+ .kv-cell { display: grid; gap: var(--sp-0); align-content: start; min-width: 0; }
75
+ .kv-cell > .icon { margin-bottom: var(--sp-1); }
76
+ .kv-cell__val { font: var(--type-h2); letter-spacing: var(--track-h2); font-variant-numeric: tabular-nums; }
77
+ .kv-cell__key { font: var(--type-caption); color: var(--card-muted); }
78
+ .card--3 .kv-cell__key { color: inherit; opacity: 0.7; }
79
+
80
+ /* ---- timeline: a hairline with date chips, entries to the right ----
81
+ <ol class="timeline"><li class="timeline__item"><span class="timeline__mark">Wed 14</span><p class="t-body">…</p></li>…</ol> */
82
+ .timeline {
83
+ list-style: none;
84
+ margin: 0;
85
+ padding: 0 0 0 var(--sp-6);
86
+ display: grid;
87
+ gap: var(--sp-5);
88
+ position: relative;
89
+ }
90
+ .timeline::before {
91
+ content: "";
92
+ position: absolute;
93
+ left: calc((var(--sp-3) - var(--border-w)) / 2); /* through the centre of the dots */
94
+ top: var(--sp-3);
95
+ bottom: var(--sp-3);
96
+ border-left: var(--border-w) solid var(--hairline);
97
+ }
98
+ .timeline__item { display: grid; gap: var(--sp-2); position: relative; min-width: 0; }
99
+ .timeline__item::before {
100
+ content: "";
101
+ position: absolute;
102
+ left: calc(-1 * var(--sp-6));
103
+ top: var(--sp-2);
104
+ width: var(--sp-3);
105
+ height: var(--sp-3);
106
+ border-radius: 50%;
107
+ background: var(--accent);
108
+ }
109
+ .timeline__mark {
110
+ justify-self: start;
111
+ font: var(--type-overline);
112
+ letter-spacing: var(--track-overline);
113
+ text-transform: uppercase;
114
+ padding: var(--sp-1) var(--sp-2);
115
+ border-radius: var(--r-chip);
116
+ background: var(--ink-tint);
117
+ }
118
+
119
+ /* ---- legend: signal dots with captions ----
120
+ <div class="legend"><span class="legend__item"><span class="dot"></span> very high</span>…</div> */
121
+ .legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); font: var(--type-caption); color: var(--card-muted); }
122
+ .card--3 .legend { color: inherit; opacity: 0.8; }
123
+ .legend__item { display: inline-flex; align-items: center; gap: var(--sp-2); min-height: var(--icon); }
124
+
125
+ /* ---- link: "See all ↗" in a card head, big enough to tap ----
126
+ <a class="link" href="#">See all <svg class="icon icon--xs"/></a> */
127
+ .link {
128
+ display: inline-flex;
129
+ align-items: center;
130
+ gap: var(--sp-1);
131
+ min-height: var(--tap-sm);
132
+ color: inherit;
133
+ text-decoration: none;
134
+ font: var(--type-label);
135
+ font-size: var(--fs-body);
136
+ border: 0;
137
+ background: transparent;
138
+ padding: 0;
139
+ cursor: pointer;
140
+ }
141
+ .link--muted { color: var(--card-muted); }
142
+ .card--3 .link--muted { color: inherit; opacity: 0.7; }