mevebu 1.3.0 → 1.4.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 (164) hide show
  1. package/mevebu/app/mevebu.js +260 -208
  2. package/mevebu/app/tailwind.css +1635 -2079
  3. package/mevebu/app/tailwind.src.css +155 -1127
  4. package/mevebu/index.html +2 -2
  5. package/mevebu/libs/js/core.js +235 -95
  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
@@ -44,17 +44,15 @@ function format(css, size) {
44
44
  }
45
45
  const rest = clean(buffer);
46
46
  if (rest) push(rest);
47
- return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
47
+ return lines.join("\n").replace(/\n{3}/g, "\n\n").trim();
48
48
  }
49
49
 
50
50
  export function mount(slot) {
51
51
  const source = textArea({
52
52
  rows: 12,
53
- class: "mvb-in mvb-in--area mvb-in--mono",
54
53
  "aria-label": "CSS",
55
- value: ".card{color:red;padding:10px 12px}.card:hover{color:blue}",
56
- });
57
- const result = textArea({ rows: 12, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Formatted CSS" });
54
+ value: ".card{color:red;padding:10px 12px}.card:hover{color:blue}"});
55
+ const result = textArea({ rows: 12, readonly: true, "aria-label": "Formatted CSS" });
58
56
  let size = "2";
59
57
 
60
58
  const chips = chipRow(
@@ -73,8 +71,7 @@ export function mount(slot) {
73
71
  stack(panel("Indent", chips), panel("CSS", source)),
74
72
  stack(panel("Formatted", result))
75
73
  ),
76
- foot: actions(copyButton(() => result.value, "Copy CSS", "fill")),
77
- });
74
+ foot: actions(copyButton(() => result.value, "Copy CSS", "fill"))});
78
75
  slot.append(card.node);
79
76
  run();
80
77
 
@@ -1,15 +1,15 @@
1
1
  // Lay out a CSS grid: columns, rows and gaps with a live preview.
2
2
  import { el, bench, stack, panel, numberInput, rangeInput, output, copyButton, actions, toolcard } from "../core.js";
3
3
 
4
- const CELL = "min-height:32px;background:var(--mvb-nebula);border:1px solid var(--mvb-comet);border-radius:6px";
4
+ const CELL = "min-height:32px;background:var(--jade-soft);border:1px solid var(--jade);border-radius:6px";
5
5
 
6
6
  function slider(label, value) {
7
7
  const input = rangeInput({ min: 0, max: 48, 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
  );
@@ -62,10 +62,9 @@ export function mount(slot) {
62
62
  const card = toolcard({
63
63
  body: bench(
64
64
  stack(panel("Columns", columns), panel("Rows", rows), columnGap, rowGap),
65
- stack(el("div", { class: "mvb-preview" }, grid), code)
65
+ stack(el("div", { class: "grid min-h-40 place-items-center overflow-hidden rounded-r1 border border-orbit bg-deck-2 p-[22px]" }, grid), code)
66
66
  ),
67
- foot: actions(copyButton(() => code.textContent, "Copy CSS", "fill")),
68
- });
67
+ foot: actions(copyButton(() => code.textContent, "Copy CSS", "fill"))});
69
68
  slot.append(card.node);
70
69
  update();
71
70
 
@@ -4,7 +4,7 @@ import { el, bench, stack, panel, textArea, switchControl, copyButton, actions,
4
4
  const bytes = (text) => new TextEncoder().encode(text).length;
5
5
 
6
6
  const tile = (key, value) =>
7
- el("div", { class: "mvb-stat" }, el("span", { class: "mvb-stat__v" }, value), el("span", { class: "mvb-stat__k" }, key));
7
+ el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, value), el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
8
8
 
9
9
  function minify(css, stripComments) {
10
10
  let out = "";
@@ -55,12 +55,10 @@ function minify(css, stripComments) {
55
55
  export function mount(slot) {
56
56
  const source = textArea({
57
57
  rows: 12,
58
- class: "mvb-in mvb-in--area mvb-in--mono",
59
58
  "aria-label": "CSS",
60
- value: "/* card */\n.card {\n color: red;\n padding: 10px 12px;\n}",
61
- });
62
- const result = textArea({ rows: 12, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Minified CSS" });
63
- const stats = el("div", { class: "mvb-stats" });
59
+ value: "/* card */\n.card {\n color: red;\n padding: 10px 12px;\n}"});
60
+ const result = textArea({ rows: 12, readonly: true, "aria-label": "Minified CSS" });
61
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
64
62
  let stripComments = true;
65
63
 
66
64
  const comments = switchControl("Remove comments", { checked: true });
@@ -87,8 +85,7 @@ export function mount(slot) {
87
85
  stack(panel("Options", comments), panel("CSS", source)),
88
86
  stack(panel("Minified", result), stats)
89
87
  ),
90
- foot: actions(copyButton(() => result.value, "Copy CSS", "fill")),
91
- });
88
+ foot: actions(copyButton(() => result.value, "Copy CSS", "fill"))});
92
89
  slot.append(card.node);
93
90
  run();
94
91
 
@@ -62,7 +62,7 @@ function coerce(value) {
62
62
  }
63
63
 
64
64
  export function mount(slot) {
65
- const source = textArea({ rows: 12, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "CSV", placeholder: "name,role\nAda,engineer" });
65
+ const source = textArea({ rows: 12, "aria-label": "CSV", placeholder: "name,role\nAda,engineer" });
66
66
  const typedBox = checkbox("Typed values", { checked: true });
67
67
  const result = output({ rows: 12 });
68
68
  const error = notice("error");
@@ -103,8 +103,7 @@ export function mount(slot) {
103
103
  stack(panel("CSV", source), panel(null, typedBox), error),
104
104
  stack(panel("JSON", result))
105
105
  ),
106
- foot: actions(copyButton(() => current, "Copy JSON", "fill")),
107
- });
106
+ foot: actions(copyButton(() => current, "Copy JSON", "fill"))});
108
107
  slot.append(card.node);
109
108
  run();
110
109
 
@@ -53,27 +53,26 @@ function detectDelimiter(text) {
53
53
  }
54
54
 
55
55
  function tile(label) {
56
- const value = el("span", { class: "mvb-stat__v" }, "0");
57
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
56
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
57
+ 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));
58
58
  node.set = (text) => { value.textContent = text; };
59
59
  return node;
60
60
  }
61
61
 
62
62
  export function mount(slot) {
63
- const source = textArea({ rows: 12, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "CSV", placeholder: "Paste CSV here" });
63
+ const source = textArea({ rows: 12, "aria-label": "CSV", placeholder: "Paste CSV here" });
64
64
  const delimiter = selectInput([
65
65
  { value: "auto", label: "Auto" },
66
66
  { value: ",", label: "Comma" },
67
67
  { value: ";", label: "Semicolon" },
68
68
  { value: "\t", label: "Tab" },
69
- { value: "|", label: "Pipe" },
70
- ], { value: "auto", "aria-label": "Delimiter" });
69
+ { value: "|", label: "Pipe" }], { value: "auto", "aria-label": "Delimiter" });
71
70
  const file = fileInput({ accept: ".csv,.tsv,.txt,text/csv", "aria-label": "CSV file" });
72
71
 
73
- const tableWrap = el("div", { class: "mvb-scroll" });
72
+ const tableWrap = el("div", { class: "max-h-80 overflow-auto" });
74
73
  const rowsTile = tile("Rows");
75
74
  const colsTile = tile("Columns");
76
- const stats = el("div", { class: "mvb-stats" }, rowsTile, colsTile);
75
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, rowsTile, colsTile);
77
76
  const error = notice("error");
78
77
  let current = [];
79
78
 
@@ -82,7 +81,7 @@ export function mount(slot) {
82
81
  const text = source.value;
83
82
  if (!text.trim()) {
84
83
  current = [];
85
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "Paste CSV or choose a file."));
84
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Paste CSV or choose a file."));
86
85
  rowsTile.set("0");
87
86
  colsTile.set("0");
88
87
  return;
@@ -97,7 +96,7 @@ export function mount(slot) {
97
96
  }
98
97
  rowsTile.set(String(body.length));
99
98
  colsTile.set(String(header.length));
100
- tableWrap.replaceChildren(el("table", { class: "mvb-ttable" },
99
+ 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" },
101
100
  el("thead", {}, el("tr", {}, ...header.map((cell, i) => el("th", {}, cell || `Column ${i + 1}`)))),
102
101
  el("tbody", {}, ...body.map((row) => el("tr", {}, ...header.map((_, i) => el("td", {}, row[i] ?? "")))))
103
102
  ));
@@ -121,8 +120,7 @@ export function mount(slot) {
121
120
  stack(panel("CSV", source), panel("Delimiter", delimiter), panel("CSV file", file), error),
122
121
  stack(stats, panel("Table", tableWrap))
123
122
  ),
124
- foot: actions(clearButton, copyButton(() => current.map((row) => row.join("\t")).join("\n"), "Copy table", "fill")),
125
- });
123
+ foot: actions(clearButton, copyButton(() => current.map((row) => row.join("\t")).join("\n"), "Copy table", "fill"))});
126
124
  slot.append(card.node);
127
125
  run();
128
126
 
@@ -12,8 +12,7 @@ const LOCALES = [
12
12
  { value: "id-ID", label: "Indonesian" },
13
13
  { value: "hi-IN", label: "Hindi" },
14
14
  { value: "ja-JP", label: "Japanese" },
15
- { value: "pt-BR", label: "Portuguese (Brazil)" },
16
- ];
15
+ { value: "pt-BR", label: "Portuguese (Brazil)" }];
17
16
 
18
17
  function format(locale, currency, value) {
19
18
  try {
@@ -28,9 +27,9 @@ export function mount(slot) {
28
27
  const currency = selectInput(CURRENCIES, { value: "USD", "aria-label": "Currency" });
29
28
  const locale = selectInput(LOCALES, { value: "en-US", "aria-label": "Locale" });
30
29
 
31
- const amount = el("span", { class: "mvb-stat__v" }, "-");
32
- const readout = el("div", { class: "mvb-stats" },
33
- el("div", { class: "mvb-stat" }, amount, el("span", { class: "mvb-stat__k" }, "Formatted")));
30
+ const amount = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
31
+ const readout = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" },
32
+ el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, amount, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Formatted")));
34
33
 
35
34
  const note = notice("note");
36
35
  note.write("The amount is formatted, not converted. No exchange rate is applied.");
@@ -58,8 +57,7 @@ export function mount(slot) {
58
57
  ),
59
58
  stack(readout, note)
60
59
  ),
61
- foot: actions(copyButton(() => formatted, "Copy amount", "fill")),
62
- });
60
+ foot: actions(copyButton(() => formatted, "Copy amount", "fill"))});
63
61
  slot.append(card.node);
64
62
  run();
65
63
 
@@ -21,20 +21,20 @@ export function mount(slot) {
21
21
  let list = Array.isArray(saved) ? saved : [];
22
22
 
23
23
  const title = textInput({ placeholder: "Activity", "aria-label": "Activity" });
24
- const start = el("input", { class: "mvb-in", type: "time", value: "09:00", "aria-label": "Start time" });
25
- const end = el("input", { class: "mvb-in", type: "time", value: "10:00", "aria-label": "End time" });
24
+ const start = el("input", { class: "min-w-0 rounded-r1 border border-orbit bg-deck text-ink transition-[border-color,box-shadow] duration-150 placeholder:text-ink-3 hover:border-orbit-hard focus:border-jade focus:outline-none focus:ring-3 focus:ring-jade-soft w-full min-h-[var(--ctl)] px-3 py-2.5 text-base", type: "time", value: "09:00", "aria-label": "Start time" });
25
+ const end = el("input", { class: "min-w-0 rounded-r1 border border-orbit bg-deck text-ink transition-[border-color,box-shadow] duration-150 placeholder:text-ink-3 hover:border-orbit-hard focus:border-jade focus:outline-none focus:ring-3 focus:ring-jade-soft w-full min-h-[var(--ctl)] px-3 py-2.5 text-base", type: "time", value: "10:00", "aria-label": "End time" });
26
26
 
27
- const listWrap = el("div", { class: "mvb-scroll" });
28
- const stats = el("div", { class: "mvb-stats" });
27
+ const listWrap = el("div", { class: "max-h-80 overflow-auto" });
28
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
29
29
  const overlapNote = notice("error");
30
30
  const error = notice("error");
31
31
 
32
32
  const addButton = button("Add", { icon: "plus", kind: "fill" });
33
33
  const clearButton = button("Clear day", { icon: "trash" });
34
34
 
35
- const tile = (key, value) => el("div", { class: "mvb-stat" },
36
- el("span", { class: "mvb-stat__v" }, String(value)),
37
- el("span", { class: "mvb-stat__k" }, key));
35
+ const tile = (key, value) => el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" },
36
+ el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, String(value)),
37
+ el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
38
38
 
39
39
  const save = () => writePin(PIN, list);
40
40
 
@@ -69,7 +69,7 @@ export function mount(slot) {
69
69
  el("td", {}, flagged.has(item.id) ? "Overlap" : ""),
70
70
  el("td", {}, removeButton(item))
71
71
  ));
72
- listWrap.replaceChildren(el("table", { class: "mvb-ttable" },
72
+ 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" },
73
73
  el("thead", {}, el("tr", {},
74
74
  el("th", {}, "Start"), el("th", {}, "End"), el("th", {}, "Activity"),
75
75
  el("th", {}, "Length"), el("th", {}, "Note"), el("th", {}, ""))),
@@ -113,8 +113,7 @@ export function mount(slot) {
113
113
  ),
114
114
  stack(stats, panel("Day", listWrap), overlapNote)
115
115
  ),
116
- foot: actions(clearButton, addButton),
117
- });
116
+ foot: actions(clearButton, addButton)});
118
117
  slot.append(card.node);
119
118
  render();
120
119
 
@@ -1,9 +1,9 @@
1
1
  // Masks emails, card numbers and phone numbers in pasted text.
2
2
  import { el, textArea, output, toggleRow, copyButton, bench, stack, panel, actions, toolcard } from "../core.js";
3
3
 
4
- const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
5
- const CARD = /\b\d[\d -]{11,}\d\b/g;
6
- const PHONE = /\b\+?\d[\d ().-]{5,}\d\b/g;
4
+ const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2}/g;
5
+ const CARD = /\b\d[\d -]{11}\d\b/g;
6
+ const PHONE = /\b\+?\d[\d ().-]{5}\d\b/g;
7
7
 
8
8
  function maskDigits(value, keep) {
9
9
  const digits = value.replace(/\D/g, "");
@@ -28,8 +28,7 @@ export function mount(slot) {
28
28
  const kinds = toggleRow([
29
29
  { value: "email", label: "Email" },
30
30
  { value: "card", label: "Card number" },
31
- { value: "phone", label: "Phone number" },
32
- ]);
31
+ { value: "phone", label: "Phone number" }]);
33
32
 
34
33
  function run() {
35
34
  const on = kinds.values();
@@ -56,12 +55,11 @@ export function mount(slot) {
56
55
  stack(
57
56
  panel("Values", source),
58
57
  panel("What to mask", kinds),
59
- el("p", { class: "mvb-note" }, "Pattern matching can miss a value, so check the result before sharing.")
58
+ el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Pattern matching can miss a value, so check the result before sharing.")
60
59
  ),
61
60
  stack(panel("Masked", result))
62
61
  ),
63
- foot: actions(copyButton(() => result.textContent, "Copy masked", "fill")),
64
- });
62
+ foot: actions(copyButton(() => result.textContent, "Copy masked", "fill"))});
65
63
  slot.append(card.node);
66
64
  run();
67
65
 
@@ -10,8 +10,7 @@ const UNITS = [
10
10
  { value: "KiB", label: "Kibibyte", factor: 1024 },
11
11
  { value: "MiB", label: "Mebibyte", factor: 1048576 },
12
12
  { value: "GiB", label: "Gibibyte", factor: 1073741824 },
13
- { value: "TiB", label: "Tebibyte", factor: 1099511627776 },
14
- ];
13
+ { value: "TiB", label: "Tebibyte", factor: 1099511627776 }];
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,13 +27,13 @@ export function mount(slot) {
28
27
  const value = numberInput({ value: "1", step: "any", "aria-label": "Value" });
29
28
  const from = selectInput(options, { value: "MB", "aria-label": "From unit" });
30
29
  const to = selectInput(options, { value: "MiB", "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 scaleNote = notice("note");
35
34
  scaleNote.write("KB is 1000 bytes, KiB is 1024 bytes.");
36
35
  const error = notice("error");
37
- const scroll = el("div", { class: "mvb-scroll" });
36
+ const scroll = el("div", { class: "max-h-80 overflow-auto" });
38
37
  let text = "";
39
38
 
40
39
  function run() {
@@ -54,7 +53,7 @@ export function mount(slot) {
54
53
  const out = (n * a.factor) / b.factor;
55
54
  result.textContent = `${fmt(out)} ${b.value}`;
56
55
  factor.textContent = `1 ${a.value} = ${fmt(a.factor / b.factor)} ${b.value}`;
57
- scroll.replaceChildren(el("table", { class: "mvb-ttable" },
56
+ 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" },
58
57
  el("thead", {}, el("tr", {}, el("th", {}, "Unit"), el("th", {}, "Value"))),
59
58
  el("tbody", {}, ...UNITS.map((unit) => el("tr", {},
60
59
  el("td", {}, unit.label),
@@ -78,8 +77,7 @@ export function mount(slot) {
78
77
  stack(panel("Value", value), panel("From", from), panel("To", to), scaleNote, error),
79
78
  stack(panel("Result", stats), factor, panel("All units", scroll))
80
79
  ),
81
- foot: actions(swap, copyButton(() => text, "Copy result", "fill")),
82
- });
80
+ foot: actions(swap, copyButton(() => text, "Copy result", "fill"))});
83
81
  slot.append(card.node);
84
82
  run();
85
83
 
@@ -2,8 +2,8 @@
2
2
  import { el, bench, stack, panel, textArea, selectInput, textInput, 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
  }
@@ -17,16 +17,15 @@ export function mount(slot) {
17
17
  { value: "number", label: "Number" },
18
18
  { value: "minlength", label: "Minimum length" },
19
19
  { value: "maxlength", label: "Maximum length" },
20
- { value: "regex", label: "Regex" },
21
- ], { value: "email", "aria-label": "Rule" });
20
+ { value: "regex", label: "Regex" }], { value: "email", "aria-label": "Rule" });
22
21
  const setting = textInput({ "aria-label": "Rule setting", placeholder: "Number" });
23
22
  const settingField = panel("Setting", setting);
24
23
 
25
- const tableWrap = el("div", { class: "mvb-scroll" });
24
+ const tableWrap = el("div", { class: "max-h-80 overflow-auto" });
26
25
  const totalTile = tile("Total");
27
26
  const validTile = tile("Valid");
28
27
  const invalidTile = tile("Invalid");
29
- const stats = el("div", { class: "mvb-stats" }, totalTile, validTile, invalidTile);
28
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, totalTile, validTile, invalidTile);
30
29
  const error = notice("error");
31
30
  let failing = [];
32
31
 
@@ -72,11 +71,11 @@ export function mount(slot) {
72
71
  failing = failures;
73
72
 
74
73
  if (!values.length) {
75
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "Paste one value per line."));
74
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Paste one value per line."));
76
75
  } else if (!failures.length) {
77
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "Every value passes."));
76
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Every value passes."));
78
77
  } else {
79
- tableWrap.replaceChildren(el("table", { class: "mvb-ttable" },
78
+ 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" },
80
79
  el("thead", {}, el("tr", {}, el("th", {}, "#"), el("th", {}, "Value"), el("th", {}, "Reason"))),
81
80
  el("tbody", {}, ...failures.map((row) => el("tr", {},
82
81
  el("th", {}, String(row.line)),
@@ -100,8 +99,7 @@ export function mount(slot) {
100
99
  stack(panel("Values", source), panel("Rule", rule), settingField, error),
101
100
  stack(stats, panel("Failed rows", tableWrap))
102
101
  ),
103
- foot: actions(copyButton(() => failing.map((row) => row.value).join("\n"), "Copy failing values", "fill")),
104
- });
102
+ foot: actions(copyButton(() => failing.map((row) => row.value).join("\n"), "Copy failing values", "fill"))});
105
103
  slot.append(card.node);
106
104
  run();
107
105
 
@@ -30,18 +30,18 @@ function calendarDiff(from, to) {
30
30
  }
31
31
 
32
32
  const kvRow = (key, value) =>
33
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
33
+ 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));
34
34
 
35
35
  export function mount(slot) {
36
36
  const start = textInput({ type: "date", value: "2026-01-01", "aria-label": "Start date" });
37
37
  const end = textInput({ type: "date", value: "2026-12-31", "aria-label": "End date" });
38
38
  const includeEnd = checkbox("Include the end date", { checked: false });
39
- const days = el("span", { class: "mvb-kv__v" }, "-");
40
- const weeks = el("span", { class: "mvb-kv__v" }, "-");
41
- const calendar = el("span", { class: "mvb-kv__v" }, "-");
39
+ const days = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
40
+ const weeks = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
41
+ const calendar = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
42
42
  const kv = el(
43
43
  "div",
44
- { class: "mvb-kv" },
44
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
45
45
  kvRow("Total days", days),
46
46
  kvRow("Weeks", weeks),
47
47
  kvRow("Calendar gap", calendar)
@@ -89,8 +89,7 @@ export function mount(slot) {
89
89
  stack(panel("Start", start), panel("End", end), includeEnd, error),
90
90
  stack(panel("Difference", kv))
91
91
  ),
92
- foot: actions(copyButton(() => text, "Copy result", "fill")),
93
- });
92
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
94
93
  slot.append(card.node);
95
94
  run();
96
95
 
@@ -4,8 +4,7 @@ import { el, bench, stack, panel, textInput, selectInput, notice, copyButton, ac
4
4
  const FORMATS = [
5
5
  { value: "iso", label: "ISO (yyyy-mm-dd)" },
6
6
  { value: "dmy", label: "Day first (dd/mm/yyyy)" },
7
- { value: "mdy", label: "Month first (mm/dd/yyyy)" },
8
- ];
7
+ { value: "mdy", label: "Month first (mm/dd/yyyy)" }];
9
8
 
10
9
  const pad = (n) => String(n).padStart(2, "0");
11
10
 
@@ -41,20 +40,20 @@ function intlFormat(date, options, fallback) {
41
40
  }
42
41
  }
43
42
 
44
- const kvRow = (key, value) => el("div", { class: "mvb-kv__row" },
45
- el("span", { class: "mvb-kv__k" }, key),
46
- el("span", { class: "mvb-kv__v" }, value));
43
+ const kvRow = (key, value) => el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" },
44
+ el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key),
45
+ el("span", { class: "wrap-anywhere font-mono text-2xs" }, value));
47
46
 
48
47
  export function mount(slot) {
49
48
  const format = selectInput(FORMATS, { value: "iso", "aria-label": "Input format" });
50
49
  const entry = textInput({ placeholder: "2026-10-15", "aria-label": "Date" });
51
50
 
52
- const iso = el("span", { class: "mvb-kv__v" }, "-");
53
- const long = el("span", { class: "mvb-kv__v" }, "-");
54
- const medium = el("span", { class: "mvb-kv__v" }, "-");
55
- const weekday = el("span", { class: "mvb-kv__v" }, "-");
56
- const unix = el("span", { class: "mvb-kv__v" }, "-");
57
- const out = el("div", { class: "mvb-kv" },
51
+ const iso = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
52
+ const long = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
53
+ const medium = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
54
+ const weekday = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
55
+ const unix = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
56
+ const out = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
58
57
  kvRow("ISO", iso),
59
58
  kvRow("Long", long),
60
59
  kvRow("Medium", medium),
@@ -96,8 +95,7 @@ export function mount(slot) {
96
95
  stack(panel("Input format", format), panel("Date", entry), error),
97
96
  stack(out, note)
98
97
  ),
99
- foot: actions(copyButton(() => isoText, "Copy ISO date", "fill")),
100
- });
98
+ foot: actions(copyButton(() => isoText, "Copy ISO date", "fill"))});
101
99
  slot.append(card.node);
102
100
  run();
103
101
 
@@ -5,8 +5,7 @@ const OPTIONS = [
5
5
  { value: ",", label: "Comma" },
6
6
  { value: ";", label: "Semicolon" },
7
7
  { value: "\t", label: "Tab" },
8
- { value: "|", label: "Pipe" },
9
- ];
8
+ { value: "|", label: "Pipe" }];
10
9
 
11
10
  function parseDelimited(text, delimiter) {
12
11
  const rows = [];
@@ -68,7 +67,7 @@ function stringifyCSV(rows, delimiter) {
68
67
  }
69
68
 
70
69
  export function mount(slot) {
71
- const source = textArea({ rows: 12, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Data", placeholder: "Paste delimited data here" });
70
+ const source = textArea({ rows: 12, "aria-label": "Data", placeholder: "Paste delimited data here" });
72
71
  const from = selectInput([{ value: "auto", label: "Auto" }, ...OPTIONS], { value: "auto", "aria-label": "Current delimiter" });
73
72
  const to = selectInput(OPTIONS, { value: ";", "aria-label": "New delimiter" });
74
73
  const result = output({ rows: 12 });
@@ -98,8 +97,7 @@ export function mount(slot) {
98
97
  stack(panel("Data", source), panel("From", from), panel("To", to), error),
99
98
  stack(panel("Result", result))
100
99
  ),
101
- foot: actions(copyButton(() => current, "Copy result", "fill")),
102
- });
100
+ foot: actions(copyButton(() => current, "Copy result", "fill"))});
103
101
  slot.append(card.node);
104
102
  run();
105
103
 
@@ -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
  }
@@ -20,7 +20,7 @@ export function mount(slot) {
20
20
  const finalTile = tile("Final price");
21
21
  const savedTile = tile("Saved");
22
22
  const effectiveTile = tile("Effective discount");
23
- const stats = el("div", { class: "mvb-stats" }, finalTile, savedTile, effectiveTile);
23
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, finalTile, savedTile, effectiveTile);
24
24
  const error = notice("error");
25
25
  let text = "";
26
26
 
@@ -72,8 +72,7 @@ export function mount(slot) {
72
72
  ),
73
73
  stack(stats)
74
74
  ),
75
- foot: actions(copyButton(() => text, "Copy result", "fill")),
76
- });
75
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
77
76
  slot.append(card.node);
78
77
  run();
79
78
 
@@ -2,8 +2,8 @@
2
2
  import { el, bench, stack, panel, textInput, notice, copyButton, actions, toolcard } from "../core.js";
3
3
 
4
4
  function kvRow(key) {
5
- const value = el("span", { class: "mvb-kv__v" }, "-");
6
- const row = el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), value);
5
+ const value = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
6
+ const row = 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), value);
7
7
  return { row, value };
8
8
  }
9
9
 
@@ -18,7 +18,7 @@ export function mount(slot) {
18
18
  const input = textInput({ placeholder: "10.1000/xyz or https://doi.org/10.1000/xyz", "aria-label": "DOI" });
19
19
  const doiRow = kvRow("Cleaned DOI");
20
20
  const linkRow = kvRow("Resolver URL");
21
- const kv = el("div", { class: "mvb-kv" }, doiRow.row, linkRow.row);
21
+ const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" }, doiRow.row, linkRow.row);
22
22
  const error = notice("error");
23
23
  const note = notice("note");
24
24
  note.write("A matching pattern does not prove the DOI is registered.");
@@ -56,8 +56,7 @@ export function mount(slot) {
56
56
  foot: actions(
57
57
  copyButton(() => doiRow.value.textContent, "Copy DOI"),
58
58
  copyButton(() => link, "Copy link", "fill")
59
- ),
60
- });
59
+ )});
61
60
  slot.append(card.node);
62
61
  run();
63
62
 
@@ -23,11 +23,9 @@ function parse(text) {
23
23
  export function mount(slot) {
24
24
  const source = textArea({
25
25
  rows: 12,
26
- class: "mvb-in mvb-in--area mvb-in--mono",
27
26
  "aria-label": "Environment variables",
28
27
  placeholder: "KEY=value",
29
- value: "API_URL=https://example.com\n# a note\nPORT=3000\nAPI_URL=https://other.com",
30
- });
28
+ value: "API_URL=https://example.com\n# a note\nPORT=3000\nAPI_URL=https://other.com"});
31
29
  const result = output({ rows: 12 });
32
30
  const warn = notice("warn");
33
31
  let sort = true;
@@ -58,11 +56,10 @@ export function mount(slot) {
58
56
 
59
57
  const card = toolcard({
60
58
  body: bench(
61
- stack(panel("Options", el("div", { class: "mvb-stack" }, sortSwitch, commentSwitch)), panel("Input", source), warn),
59
+ stack(panel("Options", el("div", { class: "grid content-start gap-4" }, sortSwitch, commentSwitch)), panel("Input", source), warn),
62
60
  stack(panel("Tidied", result))
63
61
  ),
64
- foot: actions(copyButton(() => result.textContent, "Copy list", "fill")),
65
- });
62
+ foot: actions(copyButton(() => result.textContent, "Copy list", "fill"))});
66
63
  slot.append(card.node);
67
64
  run();
68
65