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
@@ -5,8 +5,7 @@ const UNITS = [
5
5
  { value: "days", label: "Days" },
6
6
  { value: "weeks", label: "Weeks" },
7
7
  { value: "months", label: "Months" },
8
- { value: "years", label: "Years" },
9
- ];
8
+ { value: "years", label: "Years" }];
10
9
 
11
10
  const pad = (n) => String(n).padStart(2, "0");
12
11
 
@@ -24,7 +23,7 @@ function parseDate(value) {
24
23
  const iso = (date) => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
25
24
 
26
25
  const kvRow = (key, value) =>
27
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
26
+ 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));
28
27
 
29
28
  export function mount(slot) {
30
29
  const date = textInput({ type: "date", value: "2026-01-01", "aria-label": "Start date" });
@@ -35,12 +34,12 @@ export function mount(slot) {
35
34
  [{ value: "add", label: "Add" }, { value: "subtract", label: "Subtract" }],
36
35
  { value: "add", label: "Direction", onChange: (value) => { mode = value; run(); } }
37
36
  );
38
- const result = el("span", { class: "mvb-stat__v" }, "-");
39
- const tile = el("div", { class: "mvb-stat" }, result, el("span", { class: "mvb-stat__k" }, "Result"));
40
- const stats = el("div", { class: "mvb-stats" }, tile);
41
- const isoOut = el("span", { class: "mvb-kv__v" }, "-");
42
- const weekday = el("span", { class: "mvb-kv__v" }, "-");
43
- const kv = el("div", { class: "mvb-kv" }, kvRow("ISO date", isoOut), kvRow("Weekday", weekday));
37
+ const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
38
+ const tile = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, result, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Result"));
39
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, tile);
40
+ const isoOut = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
41
+ const weekday = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
42
+ const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" }, kvRow("ISO date", isoOut), kvRow("Weekday", weekday));
44
43
  const error = notice("error");
45
44
  let text = "";
46
45
 
@@ -94,8 +93,7 @@ export function mount(slot) {
94
93
  ),
95
94
  stack(stats, panel("Result", kv))
96
95
  ),
97
- foot: actions(copyButton(() => text, "Copy result", "fill")),
98
- });
96
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
99
97
  slot.append(card.node);
100
98
  run();
101
99
 
@@ -32,20 +32,20 @@ function calendarDiff(from, to) {
32
32
  }
33
33
 
34
34
  const kvRow = (key, value) =>
35
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
35
+ 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));
36
36
 
37
37
  export function mount(slot) {
38
38
  const today = new Date();
39
39
  const birth = textInput({ type: "date", value: "1990-01-01", "aria-label": "Birth date" });
40
40
  const at = textInput({ type: "date", value: `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(today.getDate())}`, "aria-label": "Age at date" });
41
41
 
42
- const age = el("span", { class: "mvb-kv__v" }, "-");
43
- const totalDays = el("span", { class: "mvb-kv__v" }, "-");
44
- const nextDate = el("span", { class: "mvb-kv__v" }, "-");
45
- const nextIn = el("span", { class: "mvb-kv__v" }, "-");
42
+ const age = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
43
+ const totalDays = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
44
+ const nextDate = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
45
+ const nextIn = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
46
46
  const kv = el(
47
47
  "div",
48
- { class: "mvb-kv" },
48
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
49
49
  kvRow("Age", age),
50
50
  kvRow("Total days", totalDays),
51
51
  kvRow("Next birthday", nextDate),
@@ -97,8 +97,7 @@ export function mount(slot) {
97
97
  stack(panel("Birth date", birth), panel("Age at", at), error),
98
98
  stack(panel("Result", kv))
99
99
  ),
100
- foot: actions(copyButton(() => text, "Copy age", "fill")),
101
- });
100
+ foot: actions(copyButton(() => text, "Copy age", "fill"))});
102
101
  slot.append(card.node);
103
102
  run();
104
103
 
@@ -36,8 +36,8 @@ function build(method, url, headers, body) {
36
36
  export function mount(slot) {
37
37
  const methodSelect = selectInput(METHODS, { value: "GET", "aria-label": "Method" });
38
38
  const urlInput = textInput({ value: "https://api.example.com/users", "aria-label": "URL", spellcheck: "false" });
39
- const headerInput = textArea({ rows: 5, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Headers", placeholder: "Content-Type: application/json", value: "Accept: application/json" });
40
- const bodyInput = textArea({ rows: 6, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Body", placeholder: '{"name":"Ada"}', value: '{\n "name": "Ada"\n}' });
39
+ const headerInput = textArea({ rows: 5, "aria-label": "Headers", placeholder: "Content-Type: application/json", value: "Accept: application/json" });
40
+ const bodyInput = textArea({ rows: 6, "aria-label": "Body", placeholder: '{"name":"Ada"}', value: '{\n "name": "Ada"\n}' });
41
41
  const result = output({ rows: 14 });
42
42
  const hint = notice("note");
43
43
 
@@ -65,8 +65,7 @@ export function mount(slot) {
65
65
  ),
66
66
  stack(panel("fetch", result))
67
67
  ),
68
- foot: actions(copyButton(() => result.textContent, "Copy snippet", "fill")),
69
- });
68
+ foot: actions(copyButton(() => result.textContent, "Copy snippet", "fill"))});
70
69
  slot.append(card.node);
71
70
  run();
72
71
 
@@ -10,8 +10,7 @@ const UNITS = [
10
10
  { value: "in2", label: "Square inch", factor: 0.00064516 },
11
11
  { value: "ft2", label: "Square foot", factor: 0.09290304 },
12
12
  { value: "yd2", label: "Square yard", factor: 0.83612736 },
13
- { value: "acre", label: "Acre", factor: 4046.8564224 },
14
- ];
13
+ { value: "acre", label: "Acre", factor: 4046.8564224 }];
15
14
 
16
15
  const byId = Object.fromEntries(UNITS.map((unit) => [unit.value, unit]));
17
16
  const options = UNITS.map(({ value, label }) => ({ value, label }));
@@ -28,11 +27,11 @@ export function mount(slot) {
28
27
  const value = numberInput({ value: "1", step: "any", "aria-label": "Value" });
29
28
  const from = selectInput(options, { value: "m2", "aria-label": "From unit" });
30
29
  const to = selectInput(options, { value: "ft2", "aria-label": "To unit" });
31
- const result = el("span", { class: "mvb-stat__v" }, "-");
32
- const stats = el("div", { class: "mvb-stats" }, el("div", { class: "mvb-stat" }, el("span", { class: "mvb-stat__k" }, "Result"), result));
33
- const factor = el("p", { class: "mvb-note" });
30
+ const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
31
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Result"), result));
32
+ const factor = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
34
33
  const error = notice("error");
35
- const scroll = el("div", { class: "mvb-scroll" });
34
+ const scroll = el("div", { class: "max-h-80 overflow-auto" });
36
35
  let text = "";
37
36
 
38
37
  function run() {
@@ -52,7 +51,7 @@ export function mount(slot) {
52
51
  const out = (n * a.factor) / b.factor;
53
52
  result.textContent = `${fmt(out)} ${b.value}`;
54
53
  factor.textContent = `1 ${a.value} = ${fmt(a.factor / b.factor)} ${b.value}`;
55
- scroll.replaceChildren(el("table", { class: "mvb-ttable" },
54
+ scroll.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
55
  el("thead", {}, el("tr", {}, el("th", {}, "Unit"), el("th", {}, "Value"))),
57
56
  el("tbody", {}, ...UNITS.map((unit) => el("tr", {},
58
57
  el("td", {}, unit.label),
@@ -76,8 +75,7 @@ export function mount(slot) {
76
75
  stack(panel("Value", value), panel("From", from), panel("To", to), error),
77
76
  stack(panel("Result", stats), factor, panel("All units", scroll))
78
77
  ),
79
- foot: actions(swap, copyButton(() => text, "Copy result", "fill")),
80
- });
78
+ foot: actions(swap, copyButton(() => text, "Copy result", "fill"))});
81
79
  slot.append(card.node);
82
80
  run();
83
81
 
@@ -8,18 +8,18 @@ export function mount(slot) {
8
8
  const width = numberInput({ value: 1920, min: "1", "aria-label": "Width" });
9
9
  const height = numberInput({ value: 1080, min: "1", "aria-label": "Height" });
10
10
 
11
- const ratioValue = el("span", { class: "mvb-kv__v" }, "-");
12
- const decimalValue = el("span", { class: "mvb-kv__v" }, "-");
11
+ const ratioValue = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
12
+ const decimalValue = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
13
13
  const list = el(
14
14
  "div",
15
- { class: "mvb-kv" },
16
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, "Ratio"), ratioValue),
17
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, "Decimal"), decimalValue)
15
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
16
+ 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" }, "Ratio"), ratioValue),
17
+ 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" }, "Decimal"), decimalValue)
18
18
  );
19
19
 
20
20
  const newWidth = numberInput({ value: 800, min: "1", "aria-label": "New width" });
21
21
  const newHeight = numberInput({ value: 450, min: "1", "aria-label": "New height" });
22
- const box = el("div", { class: "mvb-preview__box" });
22
+ const box = el("div", { class: "grid h-[120px] w-[min(200px,100%)] place-items-center bg-jade text-2xs text-on-jade" });
23
23
  const code = output();
24
24
  let ratioText = "16 / 9";
25
25
 
@@ -68,10 +68,9 @@ export function mount(slot) {
68
68
  const card = toolcard({
69
69
  body: bench(
70
70
  stack(panel("Width", width), panel("Height", height), list),
71
- stack(el("div", { class: "mvb-preview" }, box), panel("New width", newWidth), panel("New height", newHeight), code)
71
+ stack(el("div", { class: "grid min-h-40 place-items-center overflow-hidden rounded-r1 border border-orbit bg-deck-2 p-[22px]" }, box), panel("New width", newWidth), panel("New height", newHeight), code)
72
72
  ),
73
- foot: actions(copyButton(() => `aspect-ratio: ${ratioText};`, "Copy CSS", "fill")),
74
- });
73
+ foot: actions(copyButton(() => `aspect-ratio: ${ratioText};`, "Copy CSS", "fill"))});
75
74
  slot.append(card.node);
76
75
  update();
77
76
 
@@ -12,8 +12,8 @@ function parseNumbers(text) {
12
12
  }
13
13
 
14
14
  function tile(label) {
15
- const value = el("span", { class: "mvb-stat__v" }, "-");
16
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
15
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
16
+ const node = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, value, el("span", { class: "text-2xs font-semibold text-ink-3" }, label));
17
17
  node.set = (text) => { value.textContent = text; };
18
18
  return node;
19
19
  }
@@ -25,7 +25,7 @@ export function mount(slot) {
25
25
  const mode = tile("Mode");
26
26
  const count = tile("Count");
27
27
  const sum = tile("Sum");
28
- const stats = el("div", { class: "mvb-stats" }, mean, median, mode, count, sum);
28
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, mean, median, mode, count, sum);
29
29
  const error = notice("error");
30
30
  let text = "";
31
31
 
@@ -72,8 +72,7 @@ export function mount(slot) {
72
72
  stack(panel("Numbers", source), error),
73
73
  stack(stats)
74
74
  ),
75
- foot: actions(copyButton(() => text, "Copy summary", "fill")),
76
- });
75
+ foot: actions(copyButton(() => text, "Copy summary", "fill"))});
77
76
  slot.append(card.node);
78
77
  run();
79
78
 
@@ -27,7 +27,7 @@ function decode(text) {
27
27
 
28
28
  export function mount(slot) {
29
29
  const plain = textArea({ rows: 7, value: "hello mevebu", "aria-label": "Plain text" });
30
- const coded = textArea({ rows: 7, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Base64" });
30
+ const coded = textArea({ rows: 7, "aria-label": "Base64" });
31
31
  const urlSafeBox = checkbox("URL-safe, no padding", { checked: false });
32
32
  const error = notice("error");
33
33
  let guard = false;
@@ -71,8 +71,7 @@ export function mount(slot) {
71
71
  foot: actions(
72
72
  copyButton(() => plain.value, "Copy text"),
73
73
  copyButton(() => coded.value, "Copy Base64", "fill"),
74
- ),
75
- });
74
+ )});
76
75
  slot.append(card.node);
77
76
  refresh();
78
77
 
@@ -8,11 +8,11 @@ function parse(text) {
8
8
  }
9
9
 
10
10
  export function mount(slot) {
11
- const first = textInput({ value: "1011", class: "mvb-in mvb-in--mono", "aria-label": "First number" });
12
- const second = textInput({ value: "0110", class: "mvb-in mvb-in--mono", "aria-label": "Second number" });
13
- const result = el("span", { class: "mvb-stat__v" }, "-");
14
- const stats = el("div", { class: "mvb-stats" }, el("div", { class: "mvb-stat" }, el("span", { class: "mvb-stat__k" }, "Binary"), result));
15
- const decimal = el("p", { class: "mvb-note" });
11
+ const first = textInput({ value: "1011", mono: true, "aria-label": "First number" });
12
+ const second = textInput({ value: "0110", mono: true, "aria-label": "Second number" });
13
+ const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
14
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Binary"), result));
15
+ const decimal = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
16
16
  const error = notice("error");
17
17
  let mode = "add";
18
18
  let text = "";
@@ -47,8 +47,7 @@ export function mount(slot) {
47
47
  stack(panel("First number", first), panel("Second number", second), panel("Operation", chips), error),
48
48
  stack(panel("Result", stats), decimal)
49
49
  ),
50
- foot: actions(copyButton(() => text, "Copy result", "fill")),
51
- });
50
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
52
51
  slot.append(card.node);
53
52
  run();
54
53
 
@@ -5,11 +5,11 @@ const KEYS = ["tl", "tr", "br", "bl"];
5
5
 
6
6
  function slider(label, value) {
7
7
  const input = rangeInput({ min: 0, max: 100, value, "aria-label": label });
8
- const val = el("span", { class: "mvb-slider__val" }, `${value}px`);
8
+ const val = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${value}px`);
9
9
  const node = el(
10
10
  "div",
11
- { class: "mvb-slider" },
12
- el("span", { class: "mvb-slider__label" }, label),
11
+ { class: "grid items-center gap-2.5 [grid-template-columns:88px_minmax(0,1fr)_62px] max-[45rem]:[grid-template-columns:minmax(0,1fr)_62px]" },
12
+ el("span", { class: "text-2xs font-semibold text-ink-2 max-[45rem]:col-span-2" }, label),
13
13
  input,
14
14
  val
15
15
  );
@@ -27,11 +27,10 @@ export function mount(slot) {
27
27
  tl: slider("Top left", corners.tl),
28
28
  tr: slider("Top right", corners.tr),
29
29
  br: slider("Bottom right", corners.br),
30
- bl: slider("Bottom left", corners.bl),
31
- };
30
+ bl: slider("Bottom left", corners.bl)};
32
31
 
33
32
  const linkSwitch = switchControl("Link corners", { checked: true });
34
- const previewBox = el("div", { class: "mvb-preview__box" });
33
+ const previewBox = el("div", { class: "grid h-[120px] w-[min(200px,100%)] place-items-center bg-jade text-2xs text-on-jade" });
35
34
  const code = output();
36
35
 
37
36
  function css() {
@@ -75,10 +74,9 @@ export function mount(slot) {
75
74
  const card = toolcard({
76
75
  body: bench(
77
76
  stack(linkSwitch, sliders.tl, sliders.tr, sliders.br, sliders.bl),
78
- stack(el("div", { class: "mvb-preview" }, previewBox), code)
77
+ 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)
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
 
@@ -9,11 +9,11 @@ function hexToRgb(hex) {
9
9
 
10
10
  function slider(label, { min, max, value, unit = "px" }) {
11
11
  const input = rangeInput({ min, max, value, "aria-label": label });
12
- const val = el("span", { class: "mvb-slider__val" }, `${value}${unit}`);
12
+ const val = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${value}${unit}`);
13
13
  const node = el(
14
14
  "div",
15
- { class: "mvb-slider" },
16
- el("span", { class: "mvb-slider__label" }, label),
15
+ { class: "grid items-center gap-2.5 [grid-template-columns:88px_minmax(0,1fr)_62px] max-[45rem]:[grid-template-columns:minmax(0,1fr)_62px]" },
16
+ el("span", { class: "text-2xs font-semibold text-ink-2 max-[45rem]:col-span-2" }, label),
17
17
  input,
18
18
  val
19
19
  );
@@ -31,7 +31,7 @@ export function mount(slot) {
31
31
  const opacity = slider("Opacity", { min: 0, max: 100, value: 35, unit: "%" });
32
32
  const picker = colorInput({ value: "#1b1b1f", "aria-label": "Shadow color" });
33
33
  const inset = switchControl("Inset", {});
34
- const previewBox = el("div", { class: "mvb-preview__box" });
34
+ const previewBox = el("div", { class: "grid h-[120px] w-[min(200px,100%)] place-items-center bg-jade text-2xs text-on-jade" });
35
35
  const code = output();
36
36
 
37
37
  function css() {
@@ -58,10 +58,9 @@ export function mount(slot) {
58
58
  const card = toolcard({
59
59
  body: bench(
60
60
  stack(x, y, blur, spread, opacity, panel("Color", picker), inset),
61
- stack(el("div", { class: "mvb-preview" }, previewBox), code)
61
+ 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)
62
62
  ),
63
- foot: actions(copyButton(() => `box-shadow: ${css()};`, "Copy CSS", "fill")),
64
- });
63
+ foot: actions(copyButton(() => `box-shadow: ${css()};`, "Copy CSS", "fill"))});
65
64
  slot.append(card.node);
66
65
  update();
67
66
 
@@ -6,8 +6,8 @@ function fmt(n) {
6
6
  }
7
7
 
8
8
  function tile(label) {
9
- const value = el("span", { class: "mvb-stat__v" }, "-");
10
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
9
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
10
+ const node = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, value, el("span", { class: "text-2xs font-semibold text-ink-3" }, label));
11
11
  node.set = (text) => { value.textContent = text; };
12
12
  return node;
13
13
  }
@@ -19,7 +19,7 @@ export function mount(slot) {
19
19
  const unitsTile = tile("Break-even units");
20
20
  const revenueTile = tile("Revenue");
21
21
  const marginTile = tile("Contribution");
22
- const stats = el("div", { class: "mvb-stats" }, unitsTile, revenueTile, marginTile);
22
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, unitsTile, revenueTile, marginTile);
23
23
  const error = notice("error");
24
24
  let text = "";
25
25
 
@@ -57,8 +57,7 @@ export function mount(slot) {
57
57
  stack(panel("Fixed costs", fixed), panel("Price per unit", price), panel("Variable cost per unit", variable), error),
58
58
  stack(stats)
59
59
  ),
60
- foot: actions(copyButton(() => text, "Copy result", "fill")),
61
- });
60
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
62
61
  slot.append(card.node);
63
62
  run();
64
63
 
@@ -20,7 +20,7 @@ function parseDate(value) {
20
20
  const iso = (date) => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
21
21
 
22
22
  const kvRow = (key, value) =>
23
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
23
+ 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));
24
24
 
25
25
  export function mount(slot) {
26
26
  const start = textInput({ type: "date", value: "2026-01-01", "aria-label": "Start date" });
@@ -29,13 +29,13 @@ export function mount(slot) {
29
29
  const weekdays = toggleRow(DAY_LABELS.map((label, index) => ({ value: index, label, checked: index >= 1 && index <= 5 })));
30
30
  const holidays = textArea({ rows: 4, placeholder: "2026-01-01", "aria-label": "Holidays, one per line" });
31
31
 
32
- const working = el("span", { class: "mvb-kv__v" }, "-");
33
- const calendar = el("span", { class: "mvb-kv__v" }, "-");
34
- const weekend = el("span", { class: "mvb-kv__v" }, "-");
35
- const holidayCount = el("span", { class: "mvb-kv__v" }, "-");
32
+ const working = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
33
+ const calendar = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
34
+ const weekend = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
35
+ const holidayCount = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
36
36
  const kv = el(
37
37
  "div",
38
- { class: "mvb-kv" },
38
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
39
39
  kvRow("Working days", working),
40
40
  kvRow("Calendar days", calendar),
41
41
  kvRow("Weekend days", weekend),
@@ -109,8 +109,7 @@ export function mount(slot) {
109
109
  ),
110
110
  stack(panel("Holidays", holidays), panel("Count", kv))
111
111
  ),
112
- foot: actions(copyButton(() => text, "Copy result", "fill")),
113
- });
112
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
114
113
  slot.append(card.node);
115
114
  run();
116
115
 
@@ -6,15 +6,15 @@ const esc = (value) =>
6
6
  String(value).replace(/[&<>"']/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[char]));
7
7
 
8
8
  const kvRow = (key, value) =>
9
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
9
+ 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));
10
10
 
11
11
  export function mount(slot) {
12
12
  const source = textInput({ value: "https://example.com/blog/post/?utm_source=news#comments", "aria-label": "Page URL" });
13
13
  const result = output({ rows: 2 });
14
14
  const error = notice("error");
15
- const normalized = el("span", { class: "mvb-kv__v" }, "-");
16
- const length = el("span", { class: "mvb-kv__v" }, "-");
17
- const kv = el("div", { class: "mvb-kv" }, kvRow("Canonical URL", normalized), kvRow("Length", length));
15
+ const normalized = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
16
+ const length = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
17
+ const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" }, kvRow("Canonical URL", normalized), kvRow("Length", length));
18
18
  let canonical = "";
19
19
 
20
20
  function clear() {
@@ -52,8 +52,7 @@ export function mount(slot) {
52
52
  foot: actions(
53
53
  copyButton(() => canonical, "Copy URL"),
54
54
  copyButton(() => result.textContent, "Copy tag", "fill")
55
- ),
56
- });
55
+ )});
57
56
  slot.append(card.node);
58
57
  run();
59
58
 
@@ -2,8 +2,8 @@
2
2
  import { el, bench, stack, panel, textArea, numberInput, notice, copyButton, actions, toolcard } from "../core.js";
3
3
 
4
4
  function tile(label) {
5
- const value = el("span", { class: "mvb-stat__v" }, "0");
6
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
5
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
6
+ const node = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, value, el("span", { class: "text-2xs font-semibold text-ink-3" }, label));
7
7
  node.set = (text) => { value.textContent = text; };
8
8
  return node;
9
9
  }
@@ -16,7 +16,7 @@ export function mount(slot) {
16
16
  const wordsTile = tile("Words");
17
17
  const linesTile = tile("Lines");
18
18
  const leftTile = tile("Left");
19
- const stats = el("div", { class: "mvb-stats" }, charsTile, wordsTile, linesTile, leftTile);
19
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, charsTile, wordsTile, linesTile, leftTile);
20
20
  const note = notice("note");
21
21
 
22
22
  let summary = "";
@@ -43,8 +43,7 @@ export function mount(slot) {
43
43
  `${chars} characters`,
44
44
  `${words} words`,
45
45
  `${lines} lines`,
46
- cap > 0 ? `${cap - chars} left of ${cap}` : "No limit set",
47
- ].join("\n");
46
+ cap > 0 ? `${cap - chars} left of ${cap}` : "No limit set"].join("\n");
48
47
  }
49
48
 
50
49
  source.addEventListener("input", run);
@@ -55,8 +54,7 @@ export function mount(slot) {
55
54
  stack(panel("Caption", source), panel("Character limit", limit)),
56
55
  stack(stats, note)
57
56
  ),
58
- foot: actions(copyButton(() => summary, "Copy summary", "fill")),
59
- });
57
+ foot: actions(copyButton(() => summary, "Copy summary", "fill"))});
60
58
  slot.append(card.node);
61
59
  run();
62
60
 
@@ -25,15 +25,14 @@ const CASES = [
25
25
  ["snake_case", (words) => words.map((word) => word.toLowerCase()).join("_")],
26
26
  ["kebab-case", (words) => words.map((word) => word.toLowerCase()).join("-")],
27
27
  ["CONSTANT_CASE", (words) => words.map((word) => word.toUpperCase()).join("_")],
28
- ["Train-Case", (words) => words.map(cap).join("-")],
29
- ];
28
+ ["Train-Case", (words) => words.map(cap).join("-")]];
30
29
 
31
30
  export function mount(slot) {
32
31
  const PIN = "case-converter";
33
32
  const saved = readPin(PIN, { input: "Hello world from mevebu", active: "camelCase" });
34
33
 
35
34
  const source = textArea({ value: saved.input ?? "", rows: 4, "aria-label": "Text to convert", placeholder: "Type or paste text" });
36
- const list = el("div", { class: "mvb-cases" });
35
+ const list = el("div", { class: "grid gap-1" });
37
36
  let active = CASES.some(([name]) => name === saved.active) ? saved.active : "camelCase";
38
37
 
39
38
  function convert() {
@@ -41,9 +40,10 @@ export function mount(slot) {
41
40
  for (const row of list.children) {
42
41
  const convertCase = CASES.find(([name]) => name === row.dataset.case)[1];
43
42
  const value = words.length ? convertCase(words) : "";
44
- row.querySelector(".mvb-case__value").textContent = value;
43
+ row.querySelector("[data-value]").textContent = value;
45
44
  const on = row.dataset.case === active;
46
- row.classList.toggle("mvb-on", on);
45
+ if (on) row.dataset.on = "";
46
+ else delete row.dataset.on;
47
47
  row.setAttribute("aria-pressed", String(on));
48
48
  }
49
49
  writePin(PIN, { input: source.value, active });
@@ -53,16 +53,15 @@ export function mount(slot) {
53
53
  const button = el(
54
54
  "button",
55
55
  {
56
- class: "mvb-case", type: "button", dataset: { case: name }, "aria-pressed": String(name === active),
56
+ class: "group flex w-full cursor-pointer items-baseline justify-between gap-3 rounded-r1 border border-transparent bg-transparent px-3 py-[9px] text-start transition-colors duration-150 hover:bg-deck-2 data-[on]:border-jade data-[on]:bg-jade-soft", type: "button", dataset: { case: name }, "aria-pressed": String(name === active),
57
57
  onclick: async () => {
58
58
  active = name;
59
59
  convert();
60
- const value = button.querySelector(".mvb-case__value").textContent.trim();
60
+ const value = button.querySelector("[data-value]").textContent.trim();
61
61
  if (value) toast((await copyText(value)) ? "Copied" : "Copy failed");
62
- },
63
- },
64
- el("span", { class: "mvb-case__name" }, name),
65
- el("span", { class: "mvb-case__value" })
62
+ }},
63
+ el("span", { class: "flex-none text-2xs font-semibold text-ink-2 group-data-[on]:text-ink" }, name),
64
+ el("span", { class: "wrap-anywhere text-end font-mono text-sm", dataset: { value: "" } })
66
65
  );
67
66
  list.append(button);
68
67
  }
@@ -24,8 +24,7 @@ const CASES = [
24
24
  ["snake_case", (words) => words.map((word) => word.toLowerCase()).join("_")],
25
25
  ["kebab-case", (words) => words.map((word) => word.toLowerCase()).join("-")],
26
26
  ["CONSTANT_CASE", (words) => words.map((word) => word.toUpperCase()).join("_")],
27
- ["Train-Case", (words) => words.map(cap).join("-")],
28
- ];
27
+ ["Train-Case", (words) => words.map(cap).join("-")]];
29
28
 
30
29
  function detectCase(text) {
31
30
  const t = text.trim();
@@ -53,13 +52,13 @@ function detectCase(text) {
53
52
 
54
53
  export function mount(slot) {
55
54
  const source = textArea({ rows: 5, value: "helloWorld", "aria-label": "Text", placeholder: "Type or paste text" });
56
- const detected = el("span", { class: "mvb-kv__v" }, "No letters");
55
+ const detected = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "No letters");
57
56
  const kv = el(
58
57
  "div",
59
- { class: "mvb-kv" },
60
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, "Detected case"), detected)
58
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
59
+ 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" }, "Detected case"), detected)
61
60
  );
62
- const list = el("div", { class: "mvb-cases" });
61
+ const list = el("div", { class: "grid gap-1" });
63
62
  let active = "No letters";
64
63
 
65
64
  function detectedValue() {
@@ -73,9 +72,10 @@ export function mount(slot) {
73
72
  detected.textContent = active;
74
73
  for (const row of list.children) {
75
74
  const convert = CASES.find(([name]) => name === row.dataset.case)[1];
76
- row.querySelector(".mvb-case__value").textContent = words.length ? convert(words) : "";
75
+ row.querySelector("[data-value]").textContent = words.length ? convert(words) : "";
77
76
  const on = row.dataset.case === active;
78
- row.classList.toggle("mvb-on", on);
77
+ if (on) row.dataset.on = "";
78
+ else delete row.dataset.on;
79
79
  row.setAttribute("aria-pressed", String(on));
80
80
  }
81
81
  }
@@ -84,14 +84,13 @@ export function mount(slot) {
84
84
  const button = el(
85
85
  "button",
86
86
  {
87
- class: "mvb-case", type: "button", dataset: { case: name }, "aria-pressed": "false",
87
+ class: "group flex w-full cursor-pointer items-baseline justify-between gap-3 rounded-r1 border border-transparent bg-transparent px-3 py-[9px] text-start transition-colors duration-150 hover:bg-deck-2 data-[on]:border-jade data-[on]:bg-jade-soft", type: "button", dataset: { case: name }, "aria-pressed": "false",
88
88
  onclick: async () => {
89
- const value = button.querySelector(".mvb-case__value").textContent.trim();
89
+ const value = button.querySelector("[data-value]").textContent.trim();
90
90
  if (value) toast((await copyText(value)) ? "Copied" : "Copy failed");
91
- },
92
- },
93
- el("span", { class: "mvb-case__name" }, name),
94
- el("span", { class: "mvb-case__value" })
91
+ }},
92
+ el("span", { class: "flex-none text-2xs font-semibold text-ink-2 group-data-[on]:text-ink" }, name),
93
+ el("span", { class: "wrap-anywhere text-end font-mono text-sm", dataset: { value: "" } })
95
94
  );
96
95
  list.append(button);
97
96
  }
@@ -103,8 +102,7 @@ export function mount(slot) {
103
102
  stack(panel("Text", source)),
104
103
  stack(kv, panel("Cases", list))
105
104
  ),
106
- foot: actions(copyButton(detectedValue, "Copy detected", "fill")),
107
- });
105
+ foot: actions(copyButton(detectedValue, "Copy detected", "fill"))});
108
106
  slot.append(card.node);
109
107
  run();
110
108