mlola-ui 1.1.26 → 1.1.27

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.
package/README.md CHANGED
@@ -62,8 +62,9 @@ npx mlola-ui check src --json # one folder, every issue as data
62
62
  It ends with the drift: how many different colors and spacing values were
63
63
  typed by hand instead of read from the tokens, and which ones most. It works
64
64
  in any project, so it can measure one before it uses Mlola. In a Mlola
65
- project it leaves out what the CLI installed (`--all` takes the copied source
66
- in; the theme it builds, where the tokens are defined, is never checked); where
65
+ project it leaves out what the CLI installed (`--all` takes the copied markup
66
+ in; Mlola's own stylesheets, the built theme among them, are held by the
67
+ library's gates and never checked); where
67
68
  Tailwind, UnoCSS or Windi is installed it leaves utility classes alone and
68
69
  counts only the values typed into them (`bg-[#fafafa]`, `p-[13px]`).
69
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlola-ui",
3
- "version": "1.1.26",
3
+ "version": "1.1.27",
4
4
  "mcpName": "io.github.mlolahq/mlola-ui",
5
5
  "description": "Source-copy CLI that writes framework-free Mlola UI items into your project",
6
6
  "type": "module",
@@ -2,7 +2,7 @@
2
2
  "$schema": "./schema/registry-index.schema.json",
3
3
  "schemaVersion": 2,
4
4
  "name": "mlola-ui",
5
- "version": "1.1.26",
5
+ "version": "1.1.27",
6
6
  "homepage": "https://ui.mlola.com",
7
7
  "themeContract": {
8
8
  "attribute": "data-theme",
@@ -177,8 +177,8 @@
177
177
  },
178
178
  "registryDependencies": [],
179
179
  "engineDependencies": {
180
- "@mlola-ui/behavior": "^1.1.26",
181
- "@mlola-ui/icons": "^1.1.26"
180
+ "@mlola-ui/behavior": "^1.1.27",
181
+ "@mlola-ui/icons": "^1.1.27"
182
182
  },
183
183
  "options": [
184
184
  {
@@ -239,7 +239,7 @@
239
239
  },
240
240
  "registryDependencies": [],
241
241
  "engineDependencies": {
242
- "@mlola-ui/icons": "^1.1.26"
242
+ "@mlola-ui/icons": "^1.1.27"
243
243
  },
244
244
  "options": [
245
245
  {
@@ -401,7 +401,7 @@
401
401
  },
402
402
  "registryDependencies": [],
403
403
  "engineDependencies": {
404
- "@mlola-ui/icons": "^1.1.26"
404
+ "@mlola-ui/icons": "^1.1.27"
405
405
  },
406
406
  "options": [
407
407
  {
@@ -493,7 +493,7 @@
493
493
  },
494
494
  "registryDependencies": [],
495
495
  "engineDependencies": {
496
- "@mlola-ui/icons": "^1.1.26"
496
+ "@mlola-ui/icons": "^1.1.27"
497
497
  },
498
498
  "options": [],
499
499
  "variants": [],
@@ -546,7 +546,7 @@
546
546
  "spinner"
547
547
  ],
548
548
  "engineDependencies": {
549
- "@mlola-ui/motion": "^1.1.26"
549
+ "@mlola-ui/motion": "^1.1.27"
550
550
  },
551
551
  "options": [
552
552
  {
@@ -714,8 +714,8 @@
714
714
  },
715
715
  "registryDependencies": [],
716
716
  "engineDependencies": {
717
- "@mlola-ui/behavior": "^1.1.26",
718
- "@mlola-ui/icons": "^1.1.26"
717
+ "@mlola-ui/behavior": "^1.1.27",
718
+ "@mlola-ui/icons": "^1.1.27"
719
719
  },
720
720
  "options": [
721
721
  {
@@ -784,7 +784,7 @@
784
784
  },
785
785
  "registryDependencies": [],
786
786
  "engineDependencies": {
787
- "@mlola-ui/icons": "^1.1.26"
787
+ "@mlola-ui/icons": "^1.1.27"
788
788
  },
789
789
  "options": [],
790
790
  "variants": [],
@@ -839,7 +839,7 @@
839
839
  "modal"
840
840
  ],
841
841
  "engineDependencies": {
842
- "@mlola-ui/icons": "^1.1.26"
842
+ "@mlola-ui/icons": "^1.1.27"
843
843
  },
844
844
  "options": [],
845
845
  "variants": [],
@@ -903,8 +903,8 @@
903
903
  },
904
904
  "registryDependencies": [],
905
905
  "engineDependencies": {
906
- "@mlola-ui/behavior": "^1.1.26",
907
- "@mlola-ui/icons": "^1.1.26"
906
+ "@mlola-ui/behavior": "^1.1.27",
907
+ "@mlola-ui/icons": "^1.1.27"
908
908
  },
909
909
  "options": [
910
910
  {
@@ -993,7 +993,7 @@
993
993
  "input"
994
994
  ],
995
995
  "engineDependencies": {
996
- "@mlola-ui/icons": "^1.1.26"
996
+ "@mlola-ui/icons": "^1.1.27"
997
997
  },
998
998
  "options": [],
999
999
  "variants": [],
@@ -1218,8 +1218,8 @@
1218
1218
  },
1219
1219
  "registryDependencies": [],
1220
1220
  "engineDependencies": {
1221
- "@mlola-ui/behavior": "^1.1.26",
1222
- "@mlola-ui/icons": "^1.1.26"
1221
+ "@mlola-ui/behavior": "^1.1.27",
1222
+ "@mlola-ui/icons": "^1.1.27"
1223
1223
  },
1224
1224
  "options": [
1225
1225
  {
@@ -1370,7 +1370,7 @@
1370
1370
  },
1371
1371
  "registryDependencies": [],
1372
1372
  "engineDependencies": {
1373
- "@mlola-ui/icons": "^1.1.26"
1373
+ "@mlola-ui/icons": "^1.1.27"
1374
1374
  },
1375
1375
  "options": [],
1376
1376
  "variants": [],
@@ -1421,7 +1421,7 @@
1421
1421
  },
1422
1422
  "registryDependencies": [],
1423
1423
  "engineDependencies": {
1424
- "@mlola-ui/behavior": "^1.1.26"
1424
+ "@mlola-ui/behavior": "^1.1.27"
1425
1425
  },
1426
1426
  "options": [
1427
1427
  {
@@ -1799,7 +1799,7 @@
1799
1799
  },
1800
1800
  "registryDependencies": [],
1801
1801
  "engineDependencies": {
1802
- "@mlola-ui/behavior": "^1.1.26"
1802
+ "@mlola-ui/behavior": "^1.1.27"
1803
1803
  },
1804
1804
  "options": [],
1805
1805
  "variants": [],
@@ -1852,8 +1852,8 @@
1852
1852
  "input"
1853
1853
  ],
1854
1854
  "engineDependencies": {
1855
- "@mlola-ui/behavior": "^1.1.26",
1856
- "@mlola-ui/icons": "^1.1.26"
1855
+ "@mlola-ui/behavior": "^1.1.27",
1856
+ "@mlola-ui/icons": "^1.1.27"
1857
1857
  },
1858
1858
  "options": [
1859
1859
  {
@@ -1931,8 +1931,8 @@
1931
1931
  },
1932
1932
  "registryDependencies": [],
1933
1933
  "engineDependencies": {
1934
- "@mlola-ui/behavior": "^1.1.26",
1935
- "@mlola-ui/icons": "^1.1.26"
1934
+ "@mlola-ui/behavior": "^1.1.27",
1935
+ "@mlola-ui/icons": "^1.1.27"
1936
1936
  },
1937
1937
  "options": [
1938
1938
  {
@@ -2078,7 +2078,7 @@
2078
2078
  "input"
2079
2079
  ],
2080
2080
  "engineDependencies": {
2081
- "@mlola-ui/behavior": "^1.1.26"
2081
+ "@mlola-ui/behavior": "^1.1.27"
2082
2082
  },
2083
2083
  "options": [
2084
2084
  {
@@ -2223,7 +2223,7 @@
2223
2223
  },
2224
2224
  "registryDependencies": [],
2225
2225
  "engineDependencies": {
2226
- "@mlola-ui/behavior": "^1.1.26"
2226
+ "@mlola-ui/behavior": "^1.1.27"
2227
2227
  },
2228
2228
  "options": [
2229
2229
  {
@@ -2355,7 +2355,7 @@
2355
2355
  },
2356
2356
  "registryDependencies": [],
2357
2357
  "engineDependencies": {
2358
- "@mlola-ui/icons": "^1.1.26"
2358
+ "@mlola-ui/icons": "^1.1.27"
2359
2359
  },
2360
2360
  "options": [
2361
2361
  {
@@ -2443,8 +2443,8 @@
2443
2443
  "spinner"
2444
2444
  ],
2445
2445
  "engineDependencies": {
2446
- "@mlola-ui/behavior": "^1.1.26",
2447
- "@mlola-ui/icons": "^1.1.26"
2446
+ "@mlola-ui/behavior": "^1.1.27",
2447
+ "@mlola-ui/icons": "^1.1.27"
2448
2448
  },
2449
2449
  "options": [
2450
2450
  {
@@ -2599,7 +2599,7 @@
2599
2599
  },
2600
2600
  "registryDependencies": [],
2601
2601
  "engineDependencies": {
2602
- "@mlola-ui/behavior": "^1.1.26"
2602
+ "@mlola-ui/behavior": "^1.1.27"
2603
2603
  },
2604
2604
  "options": [
2605
2605
  {
@@ -2676,7 +2676,7 @@
2676
2676
  },
2677
2677
  "registryDependencies": [],
2678
2678
  "engineDependencies": {
2679
- "@mlola-ui/behavior": "^1.1.26"
2679
+ "@mlola-ui/behavior": "^1.1.27"
2680
2680
  },
2681
2681
  "options": [
2682
2682
  {
@@ -2758,7 +2758,7 @@
2758
2758
  },
2759
2759
  "registryDependencies": [],
2760
2760
  "engineDependencies": {
2761
- "@mlola-ui/icons": "^1.1.26"
2761
+ "@mlola-ui/icons": "^1.1.27"
2762
2762
  },
2763
2763
  "options": [
2764
2764
  {
@@ -2830,7 +2830,7 @@
2830
2830
  "popover"
2831
2831
  ],
2832
2832
  "engineDependencies": {
2833
- "@mlola-ui/icons": "^1.1.26"
2833
+ "@mlola-ui/icons": "^1.1.27"
2834
2834
  },
2835
2835
  "options": [
2836
2836
  {
@@ -2900,7 +2900,7 @@
2900
2900
  "popover"
2901
2901
  ],
2902
2902
  "engineDependencies": {
2903
- "@mlola-ui/icons": "^1.1.26"
2903
+ "@mlola-ui/icons": "^1.1.27"
2904
2904
  },
2905
2905
  "options": [],
2906
2906
  "variants": [],
@@ -3098,8 +3098,8 @@
3098
3098
  "input"
3099
3099
  ],
3100
3100
  "engineDependencies": {
3101
- "@mlola-ui/behavior": "^1.1.26",
3102
- "@mlola-ui/icons": "^1.1.26"
3101
+ "@mlola-ui/behavior": "^1.1.27",
3102
+ "@mlola-ui/icons": "^1.1.27"
3103
3103
  },
3104
3104
  "options": [
3105
3105
  {
@@ -3188,7 +3188,7 @@
3188
3188
  "input"
3189
3189
  ],
3190
3190
  "engineDependencies": {
3191
- "@mlola-ui/icons": "^1.1.26"
3191
+ "@mlola-ui/icons": "^1.1.27"
3192
3192
  },
3193
3193
  "options": [],
3194
3194
  "variants": [],
@@ -3243,7 +3243,7 @@
3243
3243
  "input"
3244
3244
  ],
3245
3245
  "engineDependencies": {
3246
- "@mlola-ui/icons": "^1.1.26"
3246
+ "@mlola-ui/icons": "^1.1.27"
3247
3247
  },
3248
3248
  "options": [],
3249
3249
  "variants": [],
@@ -3306,7 +3306,7 @@
3306
3306
  "popover"
3307
3307
  ],
3308
3308
  "engineDependencies": {
3309
- "@mlola-ui/icons": "^1.1.26"
3309
+ "@mlola-ui/icons": "^1.1.27"
3310
3310
  },
3311
3311
  "options": [
3312
3312
  {
@@ -3377,7 +3377,7 @@
3377
3377
  },
3378
3378
  "registryDependencies": [],
3379
3379
  "engineDependencies": {
3380
- "@mlola-ui/icons": "^1.1.26"
3380
+ "@mlola-ui/icons": "^1.1.27"
3381
3381
  },
3382
3382
  "options": [
3383
3383
  {
@@ -3496,8 +3496,8 @@
3496
3496
  },
3497
3497
  "registryDependencies": [],
3498
3498
  "engineDependencies": {
3499
- "@mlola-ui/behavior": "^1.1.26",
3500
- "@mlola-ui/icons": "^1.1.26"
3499
+ "@mlola-ui/behavior": "^1.1.27",
3500
+ "@mlola-ui/icons": "^1.1.27"
3501
3501
  },
3502
3502
  "options": [],
3503
3503
  "variants": [],
@@ -3561,7 +3561,7 @@
3561
3561
  },
3562
3562
  "registryDependencies": [],
3563
3563
  "engineDependencies": {
3564
- "@mlola-ui/behavior": "^1.1.26"
3564
+ "@mlola-ui/behavior": "^1.1.27"
3565
3565
  },
3566
3566
  "options": [
3567
3567
  {
@@ -3648,8 +3648,8 @@
3648
3648
  },
3649
3649
  "registryDependencies": [],
3650
3650
  "engineDependencies": {
3651
- "@mlola-ui/behavior": "^1.1.26",
3652
- "@mlola-ui/icons": "^1.1.26"
3651
+ "@mlola-ui/behavior": "^1.1.27",
3652
+ "@mlola-ui/icons": "^1.1.27"
3653
3653
  },
3654
3654
  "options": [],
3655
3655
  "variants": [],
@@ -3723,7 +3723,7 @@
3723
3723
  "sheet"
3724
3724
  ],
3725
3725
  "engineDependencies": {
3726
- "@mlola-ui/icons": "^1.1.26"
3726
+ "@mlola-ui/icons": "^1.1.27"
3727
3727
  },
3728
3728
  "options": [],
3729
3729
  "variants": [],
@@ -3846,7 +3846,7 @@
3846
3846
  "button"
3847
3847
  ],
3848
3848
  "engineDependencies": {
3849
- "@mlola-ui/icons": "^1.1.26"
3849
+ "@mlola-ui/icons": "^1.1.27"
3850
3850
  },
3851
3851
  "options": [
3852
3852
  {
@@ -3950,8 +3950,8 @@
3950
3950
  "input"
3951
3951
  ],
3952
3952
  "engineDependencies": {
3953
- "@mlola-ui/behavior": "^1.1.26",
3954
- "@mlola-ui/icons": "^1.1.26"
3953
+ "@mlola-ui/behavior": "^1.1.27",
3954
+ "@mlola-ui/icons": "^1.1.27"
3955
3955
  },
3956
3956
  "options": [
3957
3957
  {
package/src/check.js CHANGED
@@ -75,9 +75,11 @@ function utilityFramework(cwd) {
75
75
  }
76
76
 
77
77
  /**
78
- * What the CLI fills from Mlola itself, from mlola.config.json: `generated`
79
- * (<styles>/mlola, the engine entry and the built theme, which is where the
80
- * tokens are defined) is never checked; `copied` source is, with --all.
78
+ * What the CLI fills from Mlola itself, from mlola.config.json. Mlola's own
79
+ * stylesheets are never checked: <styles>/mlola holds the engine and the built
80
+ * theme, where the tokens are defined, and the copied stylesheets are held by
81
+ * the library's gates, which render them in every theme (a mark dimmed with
82
+ * opacity is not faded text). `copied` markup is checked with --all.
81
83
  */
82
84
  function installedByMlola(cwd) {
83
85
  const targets = readJsonIn(cwd, CONFIG_FILENAME)?.targets;
@@ -145,7 +147,8 @@ export function checkProject(cwd, { targets = [], all = false } = {}) {
145
147
  }
146
148
  const installed = installedByMlola(cwd);
147
149
  const skipped = [...installed.generated, ...(all ? [] : installed.copied)];
148
- const files = listFiles(cwd, targets.length ? targets : ["."]).filter((file) => !skipped.some((place) => within(file, place)));
150
+ const ownStylesheet = (file) => STYLES.has(path.extname(file).toLowerCase()) && installed.copied.some((place) => within(file, place));
151
+ const files = listFiles(cwd, targets.length ? targets : ["."]).filter((file) => !skipped.some((place) => within(file, place)) && !ownStylesheet(file));
149
152
  const themes = projectThemes(cwd);
150
153
  const contract = projectContract(cwd);
151
154
  const utilities = utilityFramework(cwd);
@@ -250,7 +253,7 @@ export function printReport(report, output, { github = false } = {}) {
250
253
  if (report.skipped.length) {
251
254
  output.log(
252
255
  report.all
253
- ? `Left out the stylesheets Mlola generates (${report.skipped.join(", ")}), where the tokens are defined.`
256
+ ? `Left out Mlola's own stylesheets (${report.skipped.join(", ")}, and the ones copied beside its source): the library's gates render them in every theme.`
254
257
  : `Left out what Mlola installed (${report.skipped.join(", ")}); --all checks the copied source too.`,
255
258
  );
256
259
  }
package/src/knowledge.js CHANGED
@@ -198,7 +198,6 @@ const MARGIN_UTILITY = /^-?ml-(?:\d|auto$|px$|\[)/;
198
198
  const ARBITRARY_COLOR = /-\[(#[0-9a-f]{3,8}|(?:rgba?|hsla?|oklch|oklab|lab|lch|hwb)\([^\]]*\))\]$/i;
199
199
  const ARBITRARY_SPACING = /^(?:[\w-]+:)*-?(?:[pm][trblxyse]?|gap(?:-[xy])?|space-[xy]|inset(?:-[xy])?|top|right|bottom|left)-\[(-?(?:\d*\.)?\d+(?:px|rem|em))\]$/;
200
200
  const COLORS = /#[0-9a-f]{3,8}\b|\b(?:rgba?|hsla?|oklch|oklab|lab|lch|hwb)\([^)]*\)/gi;
201
- const LENGTH = /-?(?:\d*\.)?\d+(?:px|rem|em)\b/g;
202
201
 
203
202
  /*
204
203
  * Spacing written by hand: a padding, margin, gap or inset whose length is
@@ -208,15 +207,53 @@ const LENGTH = /-?(?:\d*\.)?\d+(?:px|rem|em)\b/g;
208
207
  */
209
208
  const SPACING_CSS = /(?:^|[;{\s])((?:padding|margin|inset)(?:-(?:top|right|bottom|left|inline|block)(?:-(?:start|end))?)?|(?:row-|column-)?gap)\s*:\s*([^;}]+)/gi;
210
209
  const SPACING_JS = /(?:^|[{,\s])((?:padding|margin|inset)(?:Top|Right|Bottom|Left|Inline|Block)?(?:Start|End)?|(?:row|column)?[gG]ap)\s*:\s*("[^"]*"|'[^']*'|`[^`]*`|-?\d+(?:\.\d+)?(?=\s*[,}]))/g;
211
- const byHand = (value) => !/var\(--ml-/.test(value) && [...value.matchAll(LENGTH)].some((length) => parseFloat(length[0]) !== 0);
210
+ /** A value with every clamp(…) range taken out, nested parentheses and all. */
211
+ function withoutRanges(value) {
212
+ let out = "";
213
+ for (let index = 0; index < value.length; index += 1) {
214
+ if (/^clamp\(/i.test(value.slice(index, index + 6)) && !/[\w-]/.test(value[index - 1] ?? "")) {
215
+ let depth = 0;
216
+ let end = index + 5;
217
+ for (; end < value.length; end += 1) {
218
+ if (value[end] === "(") depth += 1;
219
+ else if (value[end] === ")" && --depth === 0) break;
220
+ }
221
+ index = end;
222
+ continue;
223
+ }
224
+ out += value[index];
225
+ }
226
+ return out;
227
+ }
212
228
 
213
- /** The lengths a hand-written spacing declaration uses (a bare JSX number is pixels), for counting drift. */
214
- const lengthsOf = (declarations) =>
215
- declarations.flatMap((declaration) => {
216
- const value = declaration.slice(declaration.indexOf(":") + 1).trim();
217
- if (/^-?\d+(?:\.\d+)?$/.test(value)) return [`${Number(value)}px`];
218
- return [...value.matchAll(LENGTH)].map((length) => length[0]).filter((length) => parseFloat(length) !== 0);
219
- });
229
+ /**
230
+ * The lengths in a value that were typed by hand: px and rem, other than 0
231
+ * and a 1px hairline, wherever they sit, beside a token or inside a calc().
232
+ * A clamp() range is fluid on purpose, and em follows the element's own font
233
+ * (optical tuning, not rhythm), so neither counts. This is the one rule for
234
+ * spacing: check_markup, `mlola-ui check` and the library's own scale audit
235
+ * (scripts/audit-scale.mjs) all read it here.
236
+ */
237
+ export function handWrittenLengths(value) {
238
+ return [...withoutRanges(String(value)).matchAll(/(?<![\w.-])-?(?:\d*\.)?\d+(?:px|rem)\b/g)]
239
+ .map((match) => match[0])
240
+ .filter((length) => {
241
+ const size = Math.abs(parseFloat(length));
242
+ return size !== 0 && !(size === 1 && length.endsWith("px"));
243
+ });
244
+ }
245
+
246
+ /** Every spacing declaration in a stylesheet (padding, margin, gap and inset), with its place. */
247
+ export function spacingDeclarations(css) {
248
+ return [...css.matchAll(SPACING_CSS)].map((match) => ({ property: match[1], value: match[2].trim(), index: match.index }));
249
+ }
250
+
251
+ /** A bare number in a JSX style object is pixels; a quoted value is read without its quotes. */
252
+ const asLength = (raw) => (/^-?\d+(?:\.\d+)?$/.test(raw) ? `${Number(raw)}px` : raw.replace(/^["'`]|["'`]$/g, ""));
253
+ const byHand = (value) => handWrittenLengths(value).length > 0;
254
+
255
+ /** The lengths a hand-written spacing declaration uses, for counting drift. */
256
+ const lengthsOf = (declarations) => declarations.flatMap((declaration) => handWrittenLengths(asLength(declaration.slice(declaration.indexOf(":") + 1).trim())));
220
257
 
221
258
  /** Text of the same length with nothing in it, so what is left keeps its offsets. */
222
259
  const blank = (text) => text.replace(/[^\n]/g, " ");
@@ -231,15 +268,11 @@ const colorsOf = (text) => [...underived(text).matchAll(COLORS)].map((match) =>
231
268
  function handSpacing(text) {
232
269
  const found = new Map();
233
270
  for (const [, property, value] of text.matchAll(SPACING_CSS)) if (byHand(value)) found.set(property, `${property}: ${value.trim()}`);
234
- for (const [, property, raw] of text.matchAll(SPACING_JS)) {
235
- // A bare number in a JSX style object is pixels.
236
- const handWritten = /^-?\d/.test(raw) ? Number(raw) !== 0 : byHand(raw.slice(1, -1));
237
- if (handWritten) found.set(property, `${property}: ${raw}`);
238
- }
271
+ for (const [, property, raw] of text.matchAll(SPACING_JS)) if (byHand(asLength(raw))) found.set(property, `${property}: ${raw}`);
239
272
  return [...found.values()];
240
273
  }
241
274
 
242
- const SPACING_FIX = "Measure with the scale: var(--ml-space-2), var(--ml-space-4)… (get_tokens spacing). A calc() or clamp() of scale steps is fine; a value invented between them is not.";
275
+ const SPACING_FIX = "Measure with the scale: var(--ml-space-2), var(--ml-space-4)… (get_tokens spacing), or a calc() of its steps. A fluid clamp() range and an em tuned to the font are fine; a length invented between the steps is not.";
243
276
 
244
277
  /** Every opening tag in HTML or JSX, with its string-valued attributes. */
245
278
  export function tagsOf(markup) {