mevebu 1.3.0 → 1.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 (164) hide show
  1. package/mevebu/app/mevebu.js +260 -208
  2. package/mevebu/app/tailwind.css +1658 -2079
  3. package/mevebu/app/tailwind.src.css +161 -1127
  4. package/mevebu/index.html +2 -2
  5. package/mevebu/libs/js/core.js +245 -96
  6. package/mevebu/libs/js/tools/abstract-checklist.js +5 -7
  7. package/mevebu/libs/js/tools/add-subtract-days.js +9 -11
  8. package/mevebu/libs/js/tools/age-calculator.js +7 -8
  9. package/mevebu/libs/js/tools/api-request-builder.js +3 -4
  10. package/mevebu/libs/js/tools/area-converter.js +7 -9
  11. package/mevebu/libs/js/tools/aspect-ratio.js +8 -9
  12. package/mevebu/libs/js/tools/average-calculator.js +4 -5
  13. package/mevebu/libs/js/tools/base64.js +2 -3
  14. package/mevebu/libs/js/tools/binary-calculator.js +6 -7
  15. package/mevebu/libs/js/tools/border-radius.js +7 -9
  16. package/mevebu/libs/js/tools/box-shadow.js +6 -7
  17. package/mevebu/libs/js/tools/break-even.js +4 -5
  18. package/mevebu/libs/js/tools/business-days.js +7 -8
  19. package/mevebu/libs/js/tools/canonical-tag.js +5 -6
  20. package/mevebu/libs/js/tools/caption-counter.js +5 -7
  21. package/mevebu/libs/js/tools/case-converter.js +10 -11
  22. package/mevebu/libs/js/tools/case-inspector.js +14 -16
  23. package/mevebu/libs/js/tools/cash-denomination.js +7 -8
  24. package/mevebu/libs/js/tools/cash-flow.js +7 -8
  25. package/mevebu/libs/js/tools/cert-expiry.js +5 -7
  26. package/mevebu/libs/js/tools/changelog-generator.js +4 -6
  27. package/mevebu/libs/js/tools/character-counter.js +10 -13
  28. package/mevebu/libs/js/tools/checklist.js +6 -7
  29. package/mevebu/libs/js/tools/citation-number-check.js +10 -13
  30. package/mevebu/libs/js/tools/coin-flip.js +9 -11
  31. package/mevebu/libs/js/tools/color-converter.js +6 -7
  32. package/mevebu/libs/js/tools/color-palette.js +6 -8
  33. package/mevebu/libs/js/tools/color-picker.js +4 -5
  34. package/mevebu/libs/js/tools/content-calendar.js +3 -4
  35. package/mevebu/libs/js/tools/contrast-checker.js +5 -6
  36. package/mevebu/libs/js/tools/cron-builder.js +4 -6
  37. package/mevebu/libs/js/tools/css-clamp.js +5 -6
  38. package/mevebu/libs/js/tools/css-formatter.js +4 -7
  39. package/mevebu/libs/js/tools/css-grid.js +6 -7
  40. package/mevebu/libs/js/tools/css-minifier.js +5 -8
  41. package/mevebu/libs/js/tools/csv-to-json.js +2 -3
  42. package/mevebu/libs/js/tools/csv-viewer.js +9 -11
  43. package/mevebu/libs/js/tools/currency-format.js +5 -7
  44. package/mevebu/libs/js/tools/daily-planner.js +9 -10
  45. package/mevebu/libs/js/tools/data-masker.js +6 -8
  46. package/mevebu/libs/js/tools/data-storage.js +7 -9
  47. package/mevebu/libs/js/tools/data-validator.js +9 -11
  48. package/mevebu/libs/js/tools/date-difference.js +6 -7
  49. package/mevebu/libs/js/tools/date-format.js +11 -13
  50. package/mevebu/libs/js/tools/delimiter-converter.js +3 -5
  51. package/mevebu/libs/js/tools/discount-calculator.js +4 -5
  52. package/mevebu/libs/js/tools/doi-resolver.js +4 -5
  53. package/mevebu/libs/js/tools/env-formatter.js +3 -6
  54. package/mevebu/libs/js/tools/equation-formatter.js +5 -9
  55. package/mevebu/libs/js/tools/expense-split.js +6 -7
  56. package/mevebu/libs/js/tools/fake-data.js +8 -13
  57. package/mevebu/libs/js/tools/find-replace.js +3 -5
  58. package/mevebu/libs/js/tools/flashcard-creator.js +9 -10
  59. package/mevebu/libs/js/tools/font-pairing.js +6 -8
  60. package/mevebu/libs/js/tools/fraction-calculator.js +10 -13
  61. package/mevebu/libs/js/tools/git-command-builder.js +11 -22
  62. package/mevebu/libs/js/tools/gpa-calculator.js +8 -10
  63. package/mevebu/libs/js/tools/grade-calculator.js +7 -8
  64. package/mevebu/libs/js/tools/gradient-generator.js +10 -12
  65. package/mevebu/libs/js/tools/habit-tracker.js +8 -9
  66. package/mevebu/libs/js/tools/hash-generator.js +3 -5
  67. package/mevebu/libs/js/tools/hashtag-formatter.js +6 -8
  68. package/mevebu/libs/js/tools/hmac-generator.js +3 -5
  69. package/mevebu/libs/js/tools/hpp-calculator.js +4 -5
  70. package/mevebu/libs/js/tools/html-entity.js +2 -3
  71. package/mevebu/libs/js/tools/html-formatter.js +3 -6
  72. package/mevebu/libs/js/tools/http-status.js +5 -7
  73. package/mevebu/libs/js/tools/inventory-quantity.js +7 -8
  74. package/mevebu/libs/js/tools/json-formatter.js +3 -6
  75. package/mevebu/libs/js/tools/json-minifier.js +5 -8
  76. package/mevebu/libs/js/tools/json-to-csv.js +3 -5
  77. package/mevebu/libs/js/tools/json-to-typescript.js +2 -5
  78. package/mevebu/libs/js/tools/json-validator.js +2 -3
  79. package/mevebu/libs/js/tools/jwt-decoder.js +6 -7
  80. package/mevebu/libs/js/tools/jwt-expiry.js +5 -6
  81. package/mevebu/libs/js/tools/jwt-generator.js +4 -6
  82. package/mevebu/libs/js/tools/length-converter.js +7 -9
  83. package/mevebu/libs/js/tools/license-reference.js +10 -13
  84. package/mevebu/libs/js/tools/line-counter.js +5 -7
  85. package/mevebu/libs/js/tools/list-difference.js +5 -7
  86. package/mevebu/libs/js/tools/list-merger.js +4 -5
  87. package/mevebu/libs/js/tools/locale-number.js +5 -8
  88. package/mevebu/libs/js/tools/markdown-preview.js +5 -8
  89. package/mevebu/libs/js/tools/markdown-table.js +5 -7
  90. package/mevebu/libs/js/tools/markup-calculator.js +4 -5
  91. package/mevebu/libs/js/tools/meeting-agenda.js +8 -9
  92. package/mevebu/libs/js/tools/meta-preview.js +12 -13
  93. package/mevebu/libs/js/tools/meta-tags.js +6 -7
  94. package/mevebu/libs/js/tools/mime-types.js +5 -7
  95. package/mevebu/libs/js/tools/name-picker.js +2 -3
  96. package/mevebu/libs/js/tools/number-base-converter.js +9 -12
  97. package/mevebu/libs/js/tools/password-generator.js +9 -12
  98. package/mevebu/libs/js/tools/password-strength.js +21 -25
  99. package/mevebu/libs/js/tools/percent-to-fraction.js +6 -7
  100. package/mevebu/libs/js/tools/percentage-calculator.js +6 -9
  101. package/mevebu/libs/js/tools/percentage-change.js +4 -5
  102. package/mevebu/libs/js/tools/permutation-combination.js +9 -12
  103. package/mevebu/libs/js/tools/prefix-suffix.js +2 -3
  104. package/mevebu/libs/js/tools/priority-matrix.js +6 -10
  105. package/mevebu/libs/js/tools/profit-margin.js +4 -5
  106. package/mevebu/libs/js/tools/qr-generator.js +3 -4
  107. package/mevebu/libs/js/tools/qr-scanner.js +11 -12
  108. package/mevebu/libs/js/tools/quadratic-solver.js +7 -8
  109. package/mevebu/libs/js/tools/query-builder.js +2 -3
  110. package/mevebu/libs/js/tools/quiz-formatter.js +4 -6
  111. package/mevebu/libs/js/tools/ratio-calculator.js +7 -8
  112. package/mevebu/libs/js/tools/readability.js +12 -15
  113. package/mevebu/libs/js/tools/reading-time.js +5 -7
  114. package/mevebu/libs/js/tools/reference-completeness.js +6 -8
  115. package/mevebu/libs/js/tools/regex-tester.js +7 -10
  116. package/mevebu/libs/js/tools/remove-duplicate-lines.js +2 -3
  117. package/mevebu/libs/js/tools/remove-extra-spaces.js +3 -5
  118. package/mevebu/libs/js/tools/robots-txt.js +3 -4
  119. package/mevebu/libs/js/tools/roman-numeral-converter.js +6 -8
  120. package/mevebu/libs/js/tools/sales-commission.js +8 -9
  121. package/mevebu/libs/js/tools/scientific-calculator.js +10 -14
  122. package/mevebu/libs/js/tools/scientific-notation.js +8 -9
  123. package/mevebu/libs/js/tools/script-planner.js +8 -10
  124. package/mevebu/libs/js/tools/selling-price.js +4 -5
  125. package/mevebu/libs/js/tools/semver-helper.js +3 -5
  126. package/mevebu/libs/js/tools/sentence-length.js +9 -10
  127. package/mevebu/libs/js/tools/sequence-analyzer.js +7 -8
  128. package/mevebu/libs/js/tools/sitemap-xml.js +4 -7
  129. package/mevebu/libs/js/tools/slug-converter.js +3 -5
  130. package/mevebu/libs/js/tools/slug-preview.js +6 -7
  131. package/mevebu/libs/js/tools/sort-lines.js +3 -5
  132. package/mevebu/libs/js/tools/speed-converter.js +7 -9
  133. package/mevebu/libs/js/tools/spin-wheel.js +19 -17
  134. package/mevebu/libs/js/tools/sql-formatter.js +5 -9
  135. package/mevebu/libs/js/tools/sql-minifier.js +5 -8
  136. package/mevebu/libs/js/tools/standard-deviation.js +6 -9
  137. package/mevebu/libs/js/tools/stopwatch.js +4 -6
  138. package/mevebu/libs/js/tools/study-schedule.js +8 -9
  139. package/mevebu/libs/js/tools/subtitle-formatter.js +10 -11
  140. package/mevebu/libs/js/tools/table-to-markdown.js +8 -12
  141. package/mevebu/libs/js/tools/tax-calculator.js +4 -5
  142. package/mevebu/libs/js/tools/temperature-converter.js +8 -11
  143. package/mevebu/libs/js/tools/text-diff.js +11 -7
  144. package/mevebu/libs/js/tools/text-direction.js +3 -5
  145. package/mevebu/libs/js/tools/text-reverser.js +3 -5
  146. package/mevebu/libs/js/tools/time-duration.js +8 -9
  147. package/mevebu/libs/js/tools/timestamp-converter.js +4 -6
  148. package/mevebu/libs/js/tools/timezone-converter.js +9 -13
  149. package/mevebu/libs/js/tools/title-length.js +6 -7
  150. package/mevebu/libs/js/tools/unicode-inspector.js +4 -5
  151. package/mevebu/libs/js/tools/url-inspector.js +8 -9
  152. package/mevebu/libs/js/tools/url-parser.js +12 -13
  153. package/mevebu/libs/js/tools/utf8-bytes.js +6 -9
  154. package/mevebu/libs/js/tools/uuid-generator.js +5 -9
  155. package/mevebu/libs/js/tools/version-compare.js +3 -4
  156. package/mevebu/libs/js/tools/video-duration.js +5 -7
  157. package/mevebu/libs/js/tools/volume-converter.js +7 -9
  158. package/mevebu/libs/js/tools/weekly-planner.js +8 -10
  159. package/mevebu/libs/js/tools/weight-converter.js +7 -9
  160. package/mevebu/libs/js/tools/weighted-average.js +4 -5
  161. package/mevebu/libs/js/tools/word-counter.js +10 -13
  162. package/mevebu/libs/js/tools/word-limit.js +4 -5
  163. package/mevebu/libs/js/tools/yaml-formatter.js +6 -7
  164. package/package.json +1 -1
@@ -8,15 +8,13 @@ const GREEK = {
8
8
  theta: "θ", iota: "ι", kappa: "κ", lambda: "λ", mu: "μ", nu: "ν", xi: "ξ",
9
9
  pi: "π", rho: "ρ", sigma: "σ", tau: "τ", phi: "φ", chi: "χ", psi: "ψ", omega: "ω",
10
10
  Gamma: "Γ", Delta: "Δ", Theta: "Θ", Lambda: "Λ", Xi: "Ξ", Pi: "Π", Sigma: "Σ",
11
- Phi: "Φ", Psi: "Ψ", Omega: "Ω",
12
- };
11
+ Phi: "Φ", Psi: "Ψ", Omega: "Ω"};
13
12
  const SYMBOLS = {
14
13
  times: "×", cdot: "·", div: "÷", pm: "±", mp: "∓", le: "≤", leq: "≤",
15
14
  ge: "≥", geq: "≥", ne: "≠", neq: "≠", approx: "≈", equiv: "≡", infty: "∞",
16
15
  sum: "Σ", prod: "Π", int: "∫", partial: "∂", nabla: "∇", rightarrow: "→",
17
16
  to: "→", leftarrow: "←", Rightarrow: "⇒", Leftarrow: "⇐", leftrightarrow: "↔",
18
- in: "∈", notin: "∉", forall: "∀", exists: "∃", angle: "∠", degree: "°",
19
- };
17
+ in: "∈", notin: "∉", forall: "∀", exists: "∃", angle: "∠", degree: "°"};
20
18
 
21
19
  const superscript = (value) => [...value].map((char) => SUP[char] ?? char).join("");
22
20
  const subscript = (value) => [...value].map((char) => SUB[char] ?? char).join("");
@@ -43,9 +41,8 @@ export function mount(slot) {
43
41
  rows: 12,
44
42
  value: "x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}",
45
43
  "aria-label": "LaTeX",
46
- placeholder: "\\frac{a}{b}",
47
- });
48
- const result = el("pre", { class: "mvb-out" });
44
+ placeholder: "\\frac{a}{b}"});
45
+ const result = el("pre", { class: "m-0 max-h-[340px] w-full overflow-auto rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3 font-mono text-sm leading-normal whitespace-pre-wrap [overflow-wrap:anywhere]" });
49
46
  const note = notice("note");
50
47
  note.write("Plain text preview, not rendered math.");
51
48
 
@@ -60,8 +57,7 @@ export function mount(slot) {
60
57
  stack(panel("LaTeX", source)),
61
58
  stack(panel("Preview", result), note)
62
59
  ),
63
- foot: actions(copyButton(() => source.value, "Copy LaTeX", "fill")),
64
- });
60
+ foot: actions(copyButton(() => source.value, "Copy LaTeX", "fill"))});
65
61
  slot.append(card.node);
66
62
  run();
67
63
 
@@ -18,7 +18,7 @@ export function mount(slot) {
18
18
  { value: mode, label: "Mode", onChange: (next) => { mode = next; save(); run(); } }
19
19
  );
20
20
  const sharesField = panel("Shares", shares);
21
- const result = el("div", { class: "mvb-scroll" });
21
+ const result = el("div", { class: "max-h-80 overflow-auto" });
22
22
  const error = notice("error");
23
23
  const remainder = notice("warn");
24
24
  let text = "";
@@ -34,7 +34,7 @@ export function mount(slot) {
34
34
  const names = people.value.split("\n").map((line) => line.trim()).filter(Boolean);
35
35
  const amount = Number(total.value);
36
36
  if (!names.length || total.value.trim() === "" || !Number.isFinite(amount) || amount < 0) {
37
- result.replaceChildren(el("p", { class: "mvb-note" }, "Add a total and one name per line."));
37
+ result.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Add a total and one name per line."));
38
38
  return;
39
39
  }
40
40
 
@@ -42,12 +42,12 @@ export function mount(slot) {
42
42
  if (mode === "custom") {
43
43
  weights = shares.value.split("\n").map((line) => line.trim()).filter(Boolean).map(Number);
44
44
  if (weights.length !== names.length || weights.some((w) => !Number.isFinite(w) || w < 0)) {
45
- result.replaceChildren(el("p", { class: "mvb-note" }, "Add one share for each name."));
45
+ result.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Add one share for each name."));
46
46
  error.write("Add one share for each name.");
47
47
  return;
48
48
  }
49
49
  if (weights.reduce((a, b) => a + b, 0) <= 0) {
50
- result.replaceChildren(el("p", { class: "mvb-note" }, "Add one share for each name."));
50
+ result.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Add one share for each name."));
51
51
  error.write("The shares add up to zero.");
52
52
  return;
53
53
  }
@@ -60,7 +60,7 @@ export function mount(slot) {
60
60
  const paid = rounded.reduce((a, b) => a + b, 0);
61
61
  const leftover = Math.round((amount - paid) * 100) / 100;
62
62
 
63
- result.replaceChildren(el("table", { class: "mvb-ttable" },
63
+ result.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
64
64
  el("thead", {}, el("tr", {}, el("th", {}, "Person"), el("th", {}, "Amount"))),
65
65
  el("tbody", {}, ...names.map((name, i) => el("tr", {},
66
66
  el("th", {}, name),
@@ -79,8 +79,7 @@ export function mount(slot) {
79
79
  stack(panel("Total", total), panel("Mode", modeChips), panel("People", people), sharesField, error),
80
80
  stack(panel("Each person", result), remainder)
81
81
  ),
82
- foot: actions(copyButton(() => text, "Copy split", "fill")),
83
- });
82
+ foot: actions(copyButton(() => text, "Copy split", "fill"))});
84
83
  slot.append(card.node);
85
84
  run();
86
85
 
@@ -7,8 +7,7 @@ const LAST = ["Ortega", "Wijaya", "Santos", "Kimura", "Novak", "Haddad", "Silva"
7
7
  const PLACES = [
8
8
  ["Jakarta", "ID", "+62"], ["Bandung", "ID", "+62"], ["Madrid", "ES", "+34"], ["Barcelona", "ES", "+34"],
9
9
  ["Austin", "US", "+1"], ["Seattle", "US", "+1"], ["Berlin", "DE", "+49"], ["Munich", "DE", "+49"],
10
- ["Osaka", "JP", "+81"], ["Tokyo", "JP", "+81"], ["Recife", "BR", "+55"], ["Lagos", "NG", "+234"],
11
- ];
10
+ ["Osaka", "JP", "+81"], ["Tokyo", "JP", "+81"], ["Recife", "BR", "+55"], ["Lagos", "NG", "+234"]];
12
11
  const pick = (list) => list[randomInt(list.length)];
13
12
 
14
13
  function person(id) {
@@ -23,8 +22,7 @@ function person(id) {
23
22
  phone: `${code} ${700 + randomInt(300)} ${1000000 + randomInt(8999999)}`,
24
23
  city,
25
24
  country,
26
- card: `4${String(randomInt(1000)).padStart(3, "0")} … ${1000 + randomInt(9000)}`,
27
- };
25
+ card: `4${String(randomInt(1000)).padStart(3, "0")} … ${1000 + randomInt(9000)}`};
28
26
  }
29
27
 
30
28
  export function mount(slot) {
@@ -34,12 +32,11 @@ export function mount(slot) {
34
32
  [
35
33
  { value: "json", label: "JSON" },
36
34
  { value: "csv", label: "CSV" },
37
- { value: "table", label: "Table" },
38
- ],
35
+ { value: "table", label: "Table" }],
39
36
  { value: "json", label: "Format", onChange: (value) => { format = value; render(); } }
40
37
  );
41
38
  const text = output({ rows: 14 });
42
- const table = el("div", { class: "mvb-scroll" });
39
+ const table = el("div", { class: "max-h-80 overflow-auto" });
43
40
  let rows = [];
44
41
 
45
42
  function totalCount() {
@@ -51,8 +48,7 @@ export function mount(slot) {
51
48
  const columns = Object.keys(rows[0] ?? {});
52
49
  return [
53
50
  columns.join("\t"),
54
- ...rows.map((row) => columns.map((key) => String(row[key])).join("\t")),
55
- ].join("\n");
51
+ ...rows.map((row) => columns.map((key) => String(row[key])).join("\t"))].join("\n");
56
52
  }
57
53
 
58
54
  function render() {
@@ -75,7 +71,7 @@ export function mount(slot) {
75
71
  }
76
72
  const head = el("tr");
77
73
  for (const key of columns) head.append(el("th", {}, key));
78
- table.replaceChildren(el("table", { class: "mvb-ttable" }, el("thead", {}, head), body));
74
+ table.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" }, el("thead", {}, head), body));
79
75
  }
80
76
  }
81
77
 
@@ -106,10 +102,9 @@ export function mount(slot) {
106
102
  const card = toolcard({
107
103
  body: bench(
108
104
  stack(panel("Format", chips), panel("Records", countField)),
109
- el("div", { class: "mvb-stack" }, text, table)
105
+ el("div", { class: "grid content-start gap-4" }, text, table)
110
106
  ),
111
- foot: actions(copyButton, saveButton, againButton),
112
- });
107
+ foot: actions(copyButton, saveButton, againButton)});
113
108
  slot.append(card.node);
114
109
  render();
115
110
 
@@ -9,9 +9,8 @@ export function mount(slot) {
9
9
  const replaceField = textInput({ placeholder: "Replace with", "aria-label": "Replace with" });
10
10
  const options = toggleRow([
11
11
  { value: "regex", label: "Regex", checked: false },
12
- { value: "ignore", label: "Ignore case", checked: false },
13
- ]);
14
- const result = textArea({ rows: 9, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Result" });
12
+ { value: "ignore", label: "Ignore case", checked: false }]);
13
+ const result = textArea({ rows: 9, readonly: true, "aria-label": "Result" });
15
14
  const error = notice("error");
16
15
  const countNote = notice("note");
17
16
 
@@ -77,8 +76,7 @@ export function mount(slot) {
77
76
  stack(panel("Text", source), panel("Find", findField), panel("Replace with", replaceField), options, error, countNote),
78
77
  stack(panel("Result", result))
79
78
  ),
80
- foot: actions(copyButton(() => result.value, "Copy result"), replaceOneButton, replaceAllButton),
81
- });
79
+ foot: actions(copyButton(() => result.value, "Copy result"), replaceOneButton, replaceAllButton)});
82
80
  slot.append(card.node);
83
81
  run();
84
82
 
@@ -12,10 +12,10 @@ export function mount(slot) {
12
12
 
13
13
  const question = textInput({ placeholder: "Question", "aria-label": "Question" });
14
14
  const answer = textArea({ rows: 3, placeholder: "Answer", "aria-label": "Answer" });
15
- const listWrap = el("div", { class: "mvb-scroll" });
16
- const stats = el("div", { class: "mvb-stats" });
17
- const face = el("pre", { class: "mvb-out" });
18
- const faceLabel = el("span", { class: "mvb-field__label" }, "Question");
15
+ const listWrap = el("div", { class: "max-h-80 overflow-auto" });
16
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
17
+ const face = el("pre", { class: "m-0 max-h-[340px] w-full overflow-auto rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3 font-mono text-sm leading-normal whitespace-pre-wrap [overflow-wrap:anywhere]" });
18
+ const faceLabel = el("span", { class: "text-sm font-semibold text-ink-2" }, "Question");
19
19
 
20
20
  const addButton = button("Add card", { icon: "plus", kind: "fill" });
21
21
  const flipButton = button("Flip", { icon: "arrow-left-right" });
@@ -23,9 +23,9 @@ export function mount(slot) {
23
23
  const nextButton = button("Next");
24
24
 
25
25
  const save = () => writePin(PIN, cards);
26
- const tile = (key, value) => el("div", { class: "mvb-stat" },
27
- el("span", { class: "mvb-stat__v" }, String(value)),
28
- el("span", { class: "mvb-stat__k" }, key));
26
+ const tile = (key, value) => el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" },
27
+ el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, String(value)),
28
+ el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
29
29
 
30
30
  const textExport = () =>
31
31
  cards.map((card, i) => `${i + 1}. Q: ${card.question}\n A: ${card.answer}`).join("\n\n");
@@ -52,7 +52,7 @@ export function mount(slot) {
52
52
  const card = cards[index];
53
53
  faceLabel.textContent = flipped ? "Answer" : "Question";
54
54
  face.textContent = flipped ? card.answer : card.question;
55
- listWrap.replaceChildren(el("table", { class: "mvb-ttable" },
55
+ listWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
56
56
  el("tbody", {}, ...cards.map((entry, i) => el("tr", {},
57
57
  el("th", {}, `${i + 1}. ${entry.question}`),
58
58
  el("td", {}, removeButton(entry)))))));
@@ -106,8 +106,7 @@ export function mount(slot) {
106
106
  copyButton(() => textExport(), "Copy text"),
107
107
  downloadButton(() => jsonExport(), "flashcards.json", "Save JSON", "application/json"),
108
108
  addButton
109
- ),
110
- });
109
+ )});
111
110
  slot.append(card.node);
112
111
  render();
113
112
 
@@ -12,16 +12,15 @@ const FONTS = [
12
12
  { value: 'Palatino, "Palatino Linotype", serif', label: "Palatino" },
13
13
  { value: "Garamond, Georgia, serif", label: "Garamond" },
14
14
  { value: '"Courier New", Courier, monospace', label: "Courier New" },
15
- { value: "ui-monospace, SFMono-Regular, Menlo, monospace", label: "Mono" },
16
- ];
15
+ { value: "ui-monospace, SFMono-Regular, Menlo, monospace", label: "Mono" }];
17
16
 
18
17
  function slider(label, { min, max, step, value, unit }) {
19
18
  const input = rangeInput({ min, max, step, value, "aria-label": label });
20
- const val = el("span", { class: "mvb-slider__val" }, `${value}${unit}`);
19
+ const val = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${value}${unit}`);
21
20
  const node = el(
22
21
  "div",
23
- { class: "mvb-slider" },
24
- el("span", { class: "mvb-slider__label" }, label),
22
+ { class: "grid items-center gap-2.5 [grid-template-columns:88px_minmax(0,1fr)_62px] max-[45rem]:[grid-template-columns:minmax(0,1fr)_62px]" },
23
+ el("span", { class: "text-2xs font-semibold text-ink-2 max-[45rem]:col-span-2" }, label),
25
24
  input,
26
25
  val
27
26
  );
@@ -46,7 +45,7 @@ export function mount(slot) {
46
45
 
47
46
  const heading = el("h2", { style: "margin:0 0 8px" }, "Heading sample");
48
47
  const para = el("p", { style: "margin:0" }, "Body text sample. The quick brown fox jumps over the lazy dog.");
49
- const sample = el("div", { class: "mvb-preview__sample" }, heading, para);
48
+ const sample = el("div", { class: "w-full rounded-lg border border-orbit bg-deck px-[18px] py-4" }, heading, para);
50
49
  const code = output();
51
50
 
52
51
  function css() {
@@ -77,8 +76,7 @@ export function mount(slot) {
77
76
  stack(panel("Heading font", headingFont), panel("Body font", bodyFont), headingSize, bodySize, lineHeight, panel("Heading weight", weightChips)),
78
77
  stack(sample, code)
79
78
  ),
80
- foot: actions(copyButton(css, "Copy CSS", "fill")),
81
- });
79
+ foot: actions(copyButton(css, "Copy CSS", "fill"))});
82
80
  slot.append(card.node);
83
81
  update();
84
82
 
@@ -17,19 +17,19 @@ function fmt(n) {
17
17
  }
18
18
 
19
19
  const kvRow = (key, value) =>
20
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
20
+ el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" }, el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key), el("span", { class: "wrap-anywhere font-mono text-2xs" }, value));
21
21
 
22
22
  export function mount(slot) {
23
23
  const n1 = numberInput({ value: "1", "aria-label": "First numerator" });
24
24
  const d1 = numberInput({ value: "2", "aria-label": "First denominator" });
25
25
  const n2 = numberInput({ value: "1", "aria-label": "Second numerator" });
26
26
  const d2 = numberInput({ value: "3", "aria-label": "Second denominator" });
27
- const resultRow = el("span", { class: "mvb-kv__v" }, "-");
28
- const decimalRow = el("span", { class: "mvb-kv__v" }, "-");
29
- const mixedRow = el("span", { class: "mvb-kv__v" }, "-");
27
+ const resultRow = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
28
+ const decimalRow = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
29
+ const mixedRow = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
30
30
  const kv = el(
31
31
  "div",
32
- { class: "mvb-kv" },
32
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
33
33
  kvRow("Fraction", resultRow),
34
34
  kvRow("Decimal", decimalRow),
35
35
  kvRow("Mixed number", mixedRow)
@@ -95,16 +95,14 @@ export function mount(slot) {
95
95
  { value: "+", label: "+" },
96
96
  { value: "-", label: "-" },
97
97
  { value: "x", label: "x" },
98
- { value: "/", label: "/" },
99
- ],
98
+ { value: "/", label: "/" }],
100
99
  {
101
100
  value: op,
102
101
  label: "Operation",
103
102
  onChange: (value) => {
104
103
  op = value;
105
104
  run();
106
- },
107
- }
105
+ }}
108
106
  );
109
107
 
110
108
  for (const input of [n1, d1, n2, d2]) input.addEventListener("input", run);
@@ -112,15 +110,14 @@ export function mount(slot) {
112
110
  const card = toolcard({
113
111
  body: bench(
114
112
  stack(
115
- panel("First fraction", el("div", { class: "mvb-row" }, n1, d1)),
113
+ panel("First fraction", el("div", { class: "flex min-w-0 flex-wrap items-center gap-2" }, n1, d1)),
116
114
  panel("Operation", chips),
117
- panel("Second fraction", el("div", { class: "mvb-row" }, n2, d2)),
115
+ panel("Second fraction", el("div", { class: "flex min-w-0 flex-wrap items-center gap-2" }, n2, d2)),
118
116
  error
119
117
  ),
120
118
  stack(panel("Result", kv))
121
119
  ),
122
- foot: actions(copyButton(() => text, "Copy fraction", "fill")),
123
- });
120
+ foot: actions(copyButton(() => text, "Copy fraction", "fill"))});
124
121
  slot.append(card.node);
125
122
  run();
126
123
 
@@ -7,56 +7,46 @@ const OPERATIONS = {
7
7
  clone: {
8
8
  label: "Clone a repository",
9
9
  fields: [["url", "Repository URL", "https://github.com/user/repo.git"], ["directory", "Directory (optional)", ""]],
10
- build: (v) => `git clone ${quote(v.url)}${v.directory ? ` ${quote(v.directory)}` : ""}`,
11
- },
10
+ build: (v) => `git clone ${quote(v.url)}${v.directory ? ` ${quote(v.directory)}` : ""}`},
12
11
  switch: {
13
12
  label: "Switch branch",
14
13
  fields: [["branch", "Branch", "main"]],
15
- build: (v) => `git switch ${quote(v.branch)}`,
16
- },
14
+ build: (v) => `git switch ${quote(v.branch)}`},
17
15
  pull: {
18
16
  label: "Pull",
19
17
  fields: [["remote", "Remote", "origin"], ["branch", "Branch (optional)", ""]],
20
- build: (v) => `git pull ${quote(v.remote)}${v.branch ? ` ${quote(v.branch)}` : ""}`,
21
- },
18
+ build: (v) => `git pull ${quote(v.remote)}${v.branch ? ` ${quote(v.branch)}` : ""}`},
22
19
  commit: {
23
20
  label: "Commit",
24
21
  fields: [["message", "Message", "Fix the thing"], ["files", "Paths (optional)", ""]],
25
- build: (v) => `git commit -m ${quote(v.message)}${v.files ? ` ${v.files}` : ""}`,
26
- },
22
+ build: (v) => `git commit -m ${quote(v.message)}${v.files ? ` ${v.files}` : ""}`},
27
23
  push: {
28
24
  label: "Push",
29
25
  fields: [["remote", "Remote", "origin"], ["branch", "Branch", "main"]],
30
- build: (v) => `git push ${quote(v.remote)} ${quote(v.branch)}`,
31
- },
26
+ build: (v) => `git push ${quote(v.remote)} ${quote(v.branch)}`},
32
27
  "push-force": {
33
28
  label: "Force push",
34
29
  fields: [["remote", "Remote", "origin"], ["branch", "Branch", "main"]],
35
30
  build: (v) => `git push --force-with-lease ${quote(v.remote)} ${quote(v.branch)}`,
36
- destructive: "Force push can overwrite commits on the remote.",
37
- },
31
+ destructive: "Force push can overwrite commits on the remote."},
38
32
  merge: {
39
33
  label: "Merge",
40
34
  fields: [["branch", "Branch", "feature"]],
41
- build: (v) => `git merge ${quote(v.branch)}`,
42
- },
35
+ build: (v) => `git merge ${quote(v.branch)}`},
43
36
  rebase: {
44
37
  label: "Rebase",
45
38
  fields: [["branch", "Branch", "main"]],
46
- build: (v) => `git rebase ${quote(v.branch)}`,
47
- },
39
+ build: (v) => `git rebase ${quote(v.branch)}`},
48
40
  reset: {
49
41
  label: "Reset hard",
50
42
  fields: [["ref", "Commit or ref", "HEAD~1"]],
51
43
  build: (v) => `git reset --hard ${quote(v.ref)}`,
52
- destructive: "Reset hard discards uncommitted work.",
53
- },
54
- };
44
+ destructive: "Reset hard discards uncommitted work."}};
55
45
 
56
46
  export function mount(slot) {
57
47
  const options = Object.entries(OPERATIONS).map(([value, config]) => ({ value, label: config.label }));
58
48
  const operationSelect = selectInput(options, { value: "clone", "aria-label": "Operation" });
59
- const fieldsHost = el("div", { class: "mvb-stack" });
49
+ const fieldsHost = el("div", { class: "grid content-start gap-4" });
60
50
  const result = output({ rows: 4 });
61
51
  const warn = notice("warn");
62
52
  const values = {};
@@ -89,8 +79,7 @@ export function mount(slot) {
89
79
  stack(panel("Operation", operationSelect), fieldsHost, warn),
90
80
  stack(panel("Command", result))
91
81
  ),
92
- foot: actions(copyButton(() => result.textContent, "Copy command", "fill")),
93
- });
82
+ foot: actions(copyButton(() => result.textContent, "Copy command", "fill"))});
94
83
  slot.append(card.node);
95
84
  paintFields();
96
85
 
@@ -7,8 +7,7 @@ const SCALE = [
7
7
  ["A+", 4.0], ["A", 4.0], ["A-", 3.7],
8
8
  ["B+", 3.3], ["B", 3.0], ["B-", 2.7],
9
9
  ["C+", 2.3], ["C", 2.0], ["C-", 1.7],
10
- ["D+", 1.3], ["D", 1.0], ["F", 0.0],
11
- ];
10
+ ["D+", 1.3], ["D", 1.0], ["F", 0.0]];
12
11
  const POINTS = new Map(SCALE);
13
12
 
14
13
  function fmt(n) {
@@ -23,15 +22,15 @@ export function mount(slot) {
23
22
  const name = textInput({ placeholder: "Course", "aria-label": "Course" });
24
23
  const credits = numberInput({ min: "0", step: "0.5", placeholder: "Credits", "aria-label": "Credits" });
25
24
  const grade = selectInput(SCALE.map(([label]) => ({ value: label, label })), { value: "A", "aria-label": "Grade" });
26
- const listWrap = el("div", { class: "mvb-scroll" });
27
- const stats = el("div", { class: "mvb-stats" });
25
+ const listWrap = el("div", { class: "max-h-80 overflow-auto" });
26
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
28
27
 
29
28
  const addButton = button("Add", { icon: "plus", kind: "fill" });
30
29
 
31
30
  const save = () => writePin(PIN, list);
32
- const tile = (key, value) => el("div", { class: "mvb-stat" },
33
- el("span", { class: "mvb-stat__v" }, String(value)),
34
- el("span", { class: "mvb-stat__k" }, key));
31
+ const tile = (key, value) => el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" },
32
+ el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, String(value)),
33
+ el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
35
34
 
36
35
  const totals = () => {
37
36
  const totalCredits = list.reduce((sum, item) => sum + item.credits, 0);
@@ -53,7 +52,7 @@ export function mount(slot) {
53
52
  if (!list.length) {
54
53
  listWrap.replaceChildren(blank("graduation-cap", "No courses yet."));
55
54
  } else {
56
- listWrap.replaceChildren(el("table", { class: "mvb-ttable" },
55
+ listWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
57
56
  el("thead", {}, el("tr", {}, el("th", {}, "Course"), el("th", {}, "Credits"), el("th", {}, "Grade"), el("th", {}, ""))),
58
57
  el("tbody", {}, ...list.map((item) => el("tr", {},
59
58
  el("th", {}, item.name),
@@ -89,8 +88,7 @@ export function mount(slot) {
89
88
  foot: actions(copyButton(() => {
90
89
  const { totalCredits, gpa } = totals();
91
90
  return gpa == null ? "" : `GPA ${fmt(gpa)}, credits ${fmt(totalCredits)}`;
92
- }, "Copy result"), addButton),
93
- });
91
+ }, "Copy result"), addButton)});
94
92
  slot.append(card.node);
95
93
  render();
96
94
 
@@ -15,16 +15,16 @@ export function mount(slot) {
15
15
  const name = textInput({ placeholder: "Component", "aria-label": "Component" });
16
16
  const score = numberInput({ min: "0", max: "100", placeholder: "Score", "aria-label": "Score" });
17
17
  const weight = numberInput({ min: "0", max: "100", placeholder: "Weight", "aria-label": "Weight" });
18
- const listWrap = el("div", { class: "mvb-scroll" });
19
- const stats = el("div", { class: "mvb-stats" });
18
+ const listWrap = el("div", { class: "max-h-80 overflow-auto" });
19
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
20
20
  const weightNote = notice("warn");
21
21
 
22
22
  const addButton = button("Add", { icon: "plus", kind: "fill" });
23
23
 
24
24
  const save = () => writePin(PIN, list);
25
- const tile = (key, value) => el("div", { class: "mvb-stat" },
26
- el("span", { class: "mvb-stat__v" }, String(value)),
27
- el("span", { class: "mvb-stat__k" }, key));
25
+ const tile = (key, value) => el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" },
26
+ el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, String(value)),
27
+ el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
28
28
 
29
29
  const totalWeight = () => list.reduce((sum, item) => sum + item.weight, 0);
30
30
  const grade = () => {
@@ -47,7 +47,7 @@ export function mount(slot) {
47
47
  if (!list.length) {
48
48
  listWrap.replaceChildren(blank("graduation-cap", "No components yet."));
49
49
  } else {
50
- listWrap.replaceChildren(el("table", { class: "mvb-ttable" },
50
+ listWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
51
51
  el("thead", {}, el("tr", {}, el("th", {}, "Component"), el("th", {}, "Score"), el("th", {}, "Weight"), el("th", {}, ""))),
52
52
  el("tbody", {}, ...list.map((item) => el("tr", {},
53
53
  el("th", {}, item.name),
@@ -90,8 +90,7 @@ export function mount(slot) {
90
90
  foot: actions(copyButton(() => {
91
91
  const result = grade();
92
92
  return result == null ? "" : `Grade ${fmt(result)}, total weight ${fmt(totalWeight())}%`;
93
- }, "Copy result"), addButton),
94
- });
93
+ }, "Copy result"), addButton)});
95
94
  slot.append(card.node);
96
95
  render();
97
96
 
@@ -6,11 +6,10 @@ export function mount(slot) {
6
6
  let angle = 90;
7
7
  const stops = [
8
8
  { color: "#f5b942", pos: 0 },
9
- { color: "#8a2be2", pos: 100 },
10
- ];
9
+ { color: "#8a2be2", pos: 100 }];
11
10
 
12
- const stopsBox = el("div", { class: "mvb-stack" });
13
- const previewBox = el("div", { class: "mvb-preview__box" });
11
+ const stopsBox = el("div", { class: "grid content-start gap-4" });
12
+ const previewBox = el("div", { class: "grid h-[120px] w-[min(200px,100%)] place-items-center bg-jade text-2xs text-on-jade" });
14
13
  const code = output();
15
14
 
16
15
  function gradientCss() {
@@ -30,7 +29,7 @@ export function mount(slot) {
30
29
 
31
30
  const range = rangeInput({ min: 0, max: 100, value: stop.pos, "aria-label": "Stop position" });
32
31
  range.style.flex = "1";
33
- const val = el("span", { class: "mvb-slider__val" }, `${stop.pos}%`);
32
+ const val = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${stop.pos}%`);
34
33
  range.addEventListener("input", () => {
35
34
  stop.pos = parseInt(range.value, 10) || 0;
36
35
  val.textContent = `${stop.pos}%`;
@@ -45,7 +44,7 @@ export function mount(slot) {
45
44
  update();
46
45
  });
47
46
 
48
- return el("div", { class: "mvb-row" }, picker, range, val, remove);
47
+ return el("div", { class: "flex min-w-0 flex-wrap items-center gap-2" }, picker, range, val, remove);
49
48
  }
50
49
 
51
50
  function paintStops() {
@@ -63,11 +62,11 @@ export function mount(slot) {
63
62
  });
64
63
 
65
64
  const angleInput = rangeInput({ min: 0, max: 360, value: angle, "aria-label": "Angle" });
66
- const angleVal = el("span", { class: "mvb-slider__val" }, `${angle}deg`);
65
+ const angleVal = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${angle}deg`);
67
66
  const angleRow = el(
68
67
  "div",
69
- { class: "mvb-slider" },
70
- el("span", { class: "mvb-slider__label" }, "Angle"),
68
+ { class: "grid items-center gap-2.5 [grid-template-columns:88px_minmax(0,1fr)_62px] max-[45rem]:[grid-template-columns:minmax(0,1fr)_62px]" },
69
+ el("span", { class: "text-2xs font-semibold text-ink-2 max-[45rem]:col-span-2" }, "Angle"),
71
70
  angleInput,
72
71
  angleVal
73
72
  );
@@ -85,10 +84,9 @@ export function mount(slot) {
85
84
  const card = toolcard({
86
85
  body: bench(
87
86
  stack(panel("Type", typeChips), angleRow, panel("Color stops", stopsBox), addStop),
88
- stack(el("div", { class: "mvb-preview" }, previewBox), code)
87
+ stack(el("div", { class: "grid min-h-40 place-items-center overflow-hidden rounded-r1 border border-orbit bg-deck-2 p-[22px]" }, previewBox), code)
89
88
  ),
90
- foot: actions(copyButton(() => `background: ${gradientCss()};`, "Copy CSS", "fill")),
91
- });
89
+ foot: actions(copyButton(() => `background: ${gradientCss()};`, "Copy CSS", "fill"))});
92
90
  slot.append(card.node);
93
91
  paintStops();
94
92
  update();
@@ -11,15 +11,15 @@ export function mount(slot) {
11
11
  let list = Array.isArray(saved) ? saved : [];
12
12
 
13
13
  const name = textInput({ placeholder: "Habit", "aria-label": "Habit" });
14
- const tableWrap = el("div", { class: "mvb-scroll" });
15
- const stats = el("div", { class: "mvb-stats" });
14
+ const tableWrap = el("div", { class: "max-h-80 overflow-auto" });
15
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
16
16
 
17
17
  const addButton = button("Add", { icon: "plus", kind: "fill" });
18
18
  const clearWeek = button("Clear week", { icon: "trash" });
19
19
 
20
- const tile = (key, value) => el("div", { class: "mvb-stat" },
21
- el("span", { class: "mvb-stat__v" }, String(value)),
22
- el("span", { class: "mvb-stat__k" }, key));
20
+ const tile = (key, value) => el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" },
21
+ el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, String(value)),
22
+ el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
23
23
 
24
24
  const save = () => writePin(PIN, list);
25
25
  const today = todayIndex();
@@ -57,7 +57,7 @@ export function mount(slot) {
57
57
 
58
58
  function render() {
59
59
  if (!list.length) {
60
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "No habits yet."));
60
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "No habits yet."));
61
61
  } else {
62
62
  const head = el("tr", {},
63
63
  el("th", {}, "Habit"),
@@ -73,7 +73,7 @@ export function mount(slot) {
73
73
  el("td", {}, String(streak(habit))),
74
74
  el("td", {}, removeButton(habit))
75
75
  ));
76
- tableWrap.replaceChildren(el("table", { class: "mvb-ttable" },
76
+ tableWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
77
77
  el("thead", {}, head),
78
78
  el("tbody", {}, ...rows)
79
79
  ));
@@ -109,8 +109,7 @@ export function mount(slot) {
109
109
  stack(panel("Habit", name)),
110
110
  stack(stats, panel("Week", tableWrap))
111
111
  ),
112
- foot: actions(clearWeek, addButton),
113
- });
112
+ foot: actions(clearWeek, addButton)});
114
113
  slot.append(card.node);
115
114
  render();
116
115