@massimo-cassandro/minimo 1.19.12 → 1.20.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.
@@ -177,7 +177,7 @@ export default {
177
177
  inline: {
178
178
  start: {
179
179
  $type: 'dimension',
180
- $value: '{size.xxs}',
180
+ $value: '{size.base}',
181
181
  }
182
182
  }
183
183
  },
@@ -173,7 +173,7 @@
173
173
  "inline": {
174
174
  "start": {
175
175
  "$type": "dimension",
176
- "$value": "{size.xxs}"
176
+ "$value": "{size.base}"
177
177
  }
178
178
  }
179
179
  }
@@ -49,6 +49,11 @@ const config = {
49
49
 
50
50
  ],
51
51
 
52
+ // If true (default), dimension token values expressed in px are converted
53
+ // to rem in the generated CSS. Values in other units (em, %, vh, dvw, ...)
54
+ // are never touched regardless of this option.
55
+ pxToRem: true,
56
+
52
57
  // If true, custom properties already present in the CSS destination file
53
58
  // (destFile, from a previous build) take priority over the ones generated
54
59
  // by this build. Properties found only in the pre-existing file (no longer
@@ -58,9 +63,20 @@ const config = {
58
63
  // Comments at the end of custom properties are preserved, while full-line ones will be lost.
59
64
  mergeCustomProps: false,
60
65
 
61
- // Properties whose names begin with one of the following strings are grouped
62
- // and placed at the beginning of the output CSS file
63
- colorPropertiesPrefixes: ['accent', 'primary', 'secondary', 'neutral'],
66
+ // Named groups of custom properties. Properties whose first hyphen-separated
67
+ // name segment matches one of a group's `prefixes` are pulled out, labelled
68
+ // with the group's `name` and placed at the beginning of the output CSS
69
+ // file, in the order the groups are listed here.
70
+ customPropsGroups: [
71
+ {
72
+ name: 'COLORS',
73
+ prefixes: ['accent', 'primary', 'secondary', 'neutral'],
74
+ },
75
+ {
76
+ name: 'LAYOUT',
77
+ prefixes: ['body', 'layout'],
78
+ },
79
+ ],
64
80
 
65
81
 
66
82
  // ---------------------------------------------------------------------------
@@ -94,7 +110,7 @@ const config = {
94
110
 
95
111
 
96
112
  // ---------------------------------------------------------------------------
97
- // CHECK-UNRESOLVED-CUSTOM-PROPS SETTINGS
113
+ // CHECK UNRESOLVED & UNUSED CUSTOM PROPS SETTINGS
98
114
  // ---------------------------------------------------------------------------
99
115
 
100
116
  // If true, also reports custom properties that are defined (in destFile or
@@ -76,16 +76,24 @@ export const penpotExpressions = VALID_EXPR_MODES.includes(buildConfig.penpotExp
76
76
  ? buildConfig.penpotExpressions
77
77
  : 'resolve';
78
78
 
79
+ // pxToRem: if true (default), dimension token values expressed in px are
80
+ // converted to rem in the generated CSS (see the size/pxToRem-smart
81
+ // transform in build-tokens-src/transforms.mjs). Values in other units
82
+ // (em, %, vh, dvw, ...) are never touched regardless of this option.
83
+ export const pxToRem = buildConfig.pxToRem !== false;
84
+
79
85
  // mergeCustomProps: if true, custom properties already present in the CSS
80
86
  // destination file take priority over the ones generated by this build (see
81
87
  // build-tokens-src/merge-css.mjs). Default: false.
82
88
  export const mergeCustomProps = buildConfig.mergeCustomProps === true;
83
89
 
84
- // colorPropertiesPrefixes: custom properties whose first hyphen-separated
85
- // name segment matches one of these prefixes are grouped together and moved
86
- // to the beginning of the generated CSS file (see formats/css.mjs). Default: [].
87
- export const colorPropertiesPrefixes = Array.isArray(buildConfig.colorPropertiesPrefixes)
88
- ? buildConfig.colorPropertiesPrefixes
90
+ // customPropsGroups: custom properties whose first hyphen-separated name
91
+ // segment matches one of a group's prefixes are pulled out, labelled with
92
+ // that group's name and moved to the beginning of the generated CSS file, in
93
+ // group-list order (see formats/css.mjs). Each entry: { name, prefixes }.
94
+ // Default: [].
95
+ export const customPropsGroups = Array.isArray(buildConfig.customPropsGroups)
96
+ ? buildConfig.customPropsGroups
89
97
  : [];
90
98
 
91
99
  // ---------------------------------------------------------------------------
@@ -131,6 +131,14 @@ const buildTypography = (tokenName, original, resolveRefs) => {
131
131
  return lines.join('\n');
132
132
  };
133
133
 
134
+ // Builds a customPropsGroups title comment, e.g.:
135
+ // /* ****** LAYOUT ****** */
136
+ // Dashes fill out a fixed total width so titles line up regardless of name length.
137
+ const DECORATOR_LENGHT = 6;
138
+ const buildGroupTitle = (name) => {
139
+ return ` /** ${'*'.repeat(DECORATOR_LENGHT)} ${name} ${'*'.repeat(DECORATOR_LENGHT)} **/`;
140
+ };
141
+
134
142
  // ── Format registration ──────────────────────────────────────────────────────
135
143
 
136
144
  // Exported counter — read by build-tokens.mjs for the final log line
@@ -239,27 +247,31 @@ StyleDictionary.registerFormat({
239
247
  const sortedProps = Object.entries(finalProps)
240
248
  .sort(([a], [b]) => a.localeCompare(b, 'en', { numeric: true, sensitivity: 'base' }));
241
249
 
242
- // colorPropertiesPrefixes: properties whose first hyphen-separated name
243
- // segment matches one of these prefixes are pulled out and placed, in
244
- // prefix-list order, at the beginning of the output file.
245
- const colorPropertiesPrefixes = options.colorPropertiesPrefixes ?? [];
246
- const colorProps = [];
250
+ // customPropsGroups: properties whose first hyphen-separated name segment
251
+ // matches one of a group's prefixes are pulled out, labelled with that
252
+ // group's name and placed, in group-list order, at the beginning of the
253
+ // output file. A property matches the first group (in list order) whose
254
+ // prefixes include it.
255
+ const customPropsGroups = options.customPropsGroups ?? [];
256
+ const groupedProps = customPropsGroups.map(() => []);
247
257
  const restProps = [];
248
258
  for (const entry of sortedProps) {
249
259
  const prefix = entry[0].split('-')[0];
250
- if (colorPropertiesPrefixes.includes(prefix)) {
251
- colorProps.push(entry);
260
+ const groupIndex = customPropsGroups.findIndex((g) => g.prefixes?.includes(prefix));
261
+ if (groupIndex !== -1) {
262
+ groupedProps[groupIndex].push(entry);
252
263
  } else {
253
264
  restProps.push(entry);
254
265
  }
255
266
  }
256
- colorProps.sort(([a], [b]) => {
257
- const prefixA = a.split('-')[0];
258
- const prefixB = b.split('-')[0];
259
- const idxA = colorPropertiesPrefixes.indexOf(prefixA);
260
- const idxB = colorPropertiesPrefixes.indexOf(prefixB);
261
- if (idxA !== idxB) return idxA - idxB;
262
- return a.localeCompare(b, 'en', { numeric: true, sensitivity: 'base' });
267
+ groupedProps.forEach((props, i) => {
268
+ const prefixes = customPropsGroups[i].prefixes;
269
+ props.sort(([a], [b]) => {
270
+ const idxA = prefixes.indexOf(a.split('-')[0]);
271
+ const idxB = prefixes.indexOf(b.split('-')[0]);
272
+ if (idxA !== idxB) return idxA - idxB;
273
+ return a.localeCompare(b, 'en', { numeric: true, sensitivity: 'base' });
274
+ });
263
275
  });
264
276
 
265
277
  // A blank line is inserted between blocks of properties sharing the same
@@ -278,11 +290,17 @@ StyleDictionary.registerFormat({
278
290
  }
279
291
  };
280
292
 
281
- if (colorProps.length) {
282
- outLines.push(' /** COLORS **/');
283
- }
284
- appendBlock(colorProps);
285
- if (colorProps.length && restProps.length) {
293
+ const hasGroups = groupedProps.some((props) => props.length);
294
+ let isFirstBlock = true;
295
+ groupedProps.forEach((props, i) => {
296
+ if (!props.length) return;
297
+ if (!isFirstBlock) outLines.push('');
298
+ isFirstBlock = false;
299
+ outLines.push(buildGroupTitle(customPropsGroups[i].name));
300
+ appendBlock(props);
301
+ prevPrefix = null;
302
+ });
303
+ if (hasGroups && restProps.length) {
286
304
  outLines.push('', ' /* ---------------------- */', '');
287
305
  prevPrefix = null;
288
306
  }
@@ -44,7 +44,8 @@ const PENPOT_TRANSFORMS = [
44
44
  * Collected from sd.allTokens after SD initialisation.
45
45
  * Ignored when penpotDestFile is set.
46
46
  * @param {'keep'|'calc'|'resolve'} opts.penpotExpressions How to handle math expressions in dimension tokens.
47
- * @param {string[]} opts.colorPropertiesPrefixes Name prefixes grouped at the top of the CSS output.
47
+ * @param {{name: string, prefixes: string[]}[]} opts.customPropsGroups Named groups of name prefixes, moved to the top of the CSS output in list order.
48
+ * @param {boolean} opts.pxToRem If false, skips the px→rem transform on the CSS platform. Default: true.
48
49
  * @returns {Record<string, PlatformConfig>} platforms object ready for Style Dictionary config
49
50
  */
50
51
  export const buildPlatforms = ({
@@ -55,13 +56,18 @@ export const buildPlatforms = ({
55
56
  penpotFormat,
56
57
  penpotExpressions = 'keep',
57
58
  concreteFilePaths = [],
58
- colorPropertiesPrefixes = [],
59
+ customPropsGroups = [],
60
+ pxToRem = true,
59
61
  }) => {
62
+ const cssTransforms = pxToRem
63
+ ? CSS_TRANSFORMS
64
+ : CSS_TRANSFORMS.filter((name) => name !== 'size/pxToRem-smart');
65
+
60
66
  /** @type {Record<string, PlatformConfig>} */
61
67
  const platforms = {
62
68
  css: {
63
69
  buildPath: buildPath + '/',
64
- transforms: CSS_TRANSFORMS,
70
+ transforms: cssTransforms,
65
71
  files: [
66
72
  {
67
73
  destination: destFile,
@@ -69,7 +75,7 @@ export const buildPlatforms = ({
69
75
  options: {
70
76
  outputReferences: true,
71
77
  showFileHeader: true,
72
- colorPropertiesPrefixes,
78
+ customPropsGroups,
73
79
  },
74
80
  },
75
81
  ],
@@ -55,7 +55,7 @@ StyleDictionary.registerTransform({
55
55
  });
56
56
 
57
57
  // ---------------------------------------------------------------------------
58
- // 2. px → rem (dimension tokens only, skips values already in rem)
58
+ // 2. px → rem (dimension tokens only, skips values already in rem, %, em, ...)
59
59
  // ---------------------------------------------------------------------------
60
60
  StyleDictionary.registerTransform({
61
61
  name: 'size/pxToRem-smart',
@@ -64,8 +64,7 @@ StyleDictionary.registerTransform({
64
64
  const value = token.$value || token.value;
65
65
  return (token.$type === 'dimension' || token.type === 'dimension')
66
66
  && typeof value === 'string'
67
- && !value.endsWith('rem')
68
- && !value.endsWith('%');
67
+ && value.endsWith('px');
69
68
  },
70
69
  transform: (token) => {
71
70
  const value = token.$value || token.value;
@@ -32,7 +32,8 @@ import {
32
32
  penpotExpressions,
33
33
  source,
34
34
  mergeCustomProps,
35
- colorPropertiesPrefixes,
35
+ customPropsGroups,
36
+ pxToRem,
36
37
  } from './build-tokens-src/config.mjs';
37
38
 
38
39
  // ── 2. Custom transforms ─────────────────────────────────────────────────────
@@ -96,7 +97,8 @@ const sd = new StyleDictionary({
96
97
  penpotFormat,
97
98
  penpotExpressions,
98
99
  concreteFilePaths,
99
- colorPropertiesPrefixes,
100
+ customPropsGroups,
101
+ pxToRem,
100
102
  }),
101
103
  });
102
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@massimo-cassandro/minimo",
3
- "version": "1.19.12",
3
+ "version": "1.20.1",
4
4
  "description": "Light css framework & utilities",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -37,6 +37,8 @@
37
37
  "upd@m": "npx upd@m",
38
38
  "build-tokens": "node ./design-tokens/utilities/build-tokens.mjs --config ./design-tokens/tokens-config.mjs",
39
39
  "build-margin-padding-css": "node ./scripts/build-margin-padding-css.mjs",
40
+ "generate:types": "node ./scripts/generate-class-types.mjs",
41
+ "prepublishOnly": "npm run generate:types",
40
42
  "check unresolved props": "node ./design-tokens/utilities/check-unresolved-custom-props.mjs --config ./design-tokens/tokens-config.mjs",
41
43
  "test": "node --test \"test/**/*.test.mjs\"",
42
44
  "test sideEffects tree-shaking": "node ./test/sideEffects-tree-shaking/run-test.mjs",
@@ -56,6 +58,7 @@
56
58
  "files": [
57
59
  "./index.js",
58
60
  "src/**/*.{js,mjs,css,svg,md}",
61
+ "types/**/*.d.ts",
59
62
  "design-tokens/README.md",
60
63
  "design-tokens/tokens-config-sample.mjs",
61
64
  "design-tokens/tokens/**/*.{tokens.json,tokens.jsonc}",
@@ -192,7 +192,7 @@ export function modalAlert(params = {}) {
192
192
  ? 'cancel'
193
193
  : 'ok';
194
194
 
195
- console.log(params);
195
+ // console.log(params);
196
196
 
197
197
  const dialog = /** @type {HTMLDialogElement} */ (domBuilder([
198
198
  {
@@ -305,7 +305,7 @@ export function modalAlert(params = {}) {
305
305
  arg = btn.dataset.malertResult;
306
306
 
307
307
  } else if(params.type === 'confirm') {
308
- arg = btn.classList.contains('malert-ok');
308
+ arg = btn === okBtn;
309
309
  }
310
310
  }
311
311
 
@@ -1,5 +1,5 @@
1
1
  :root {
2
- /** COLORS **/
2
+ /** ****** COLORS ****** **/
3
3
  --accent: #567d0d;
4
4
  --accent-fg: #fff;
5
5
 
@@ -12,6 +12,12 @@
12
12
  --neutral-100: #666;
13
13
  --neutral-100-fg: #fff;
14
14
 
15
+ /** ****** LAYOUT ****** **/
16
+ --body-background-color: #fff;
17
+ --body-text: var(--text-color);
18
+
19
+ --layout-container-max-width: 80rem;
20
+
15
21
  /* ---------------------- */
16
22
 
17
23
  --alert-border-radius: var(--radius-sm);
@@ -24,9 +30,6 @@
24
30
  --badge-lg-font-size: var(--font-size-base);
25
31
  --badge-lg-font-weight: var(--badge-font-weight);
26
32
 
27
- --body-background-color: #fff;
28
- --body-text: var(--text-color);
29
-
30
33
  --box-shadow-base: 0 0 .75rem 0 rgb(0 0 0 / .25);
31
34
 
32
35
  --btn-border-radius: var(--form-control-border-radius);
@@ -162,10 +165,10 @@
162
165
  --form-control-readonly-opacity: 1;
163
166
  --form-control-required-flag-color: #c00;
164
167
  --form-control-required-flag-content: '\002A';
165
- --form-control-required-flag-font-size: .125rem;
166
- --form-control-required-flag-left: 0rem;
167
- --form-control-required-flag-top: -.009375rem;
168
- --form-control-required-label-padding-inline-start: var(--size-xxs);
168
+ --form-control-required-flag-font-size: 2em;
169
+ --form-control-required-flag-left: 0em;
170
+ --form-control-required-flag-top: -.15em;
171
+ --form-control-required-label-padding-inline-start: var(--size-base);
169
172
  --form-control-select-trigger-icon: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22rgb(5%2C%2048%2C%2056)%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m2%205%206%206%206-6%22%2F%3E%3C%2Fsvg%3E');
170
173
  --form-control-sm-font-size: var(--font-size-sm);
171
174
  --form-control-sm-padding-block: var(--size-xxs);
@@ -211,8 +214,6 @@
211
214
  --inner-nav-gap: var(--size-lg);
212
215
  --inner-nav-margin-block: var(--size-base);
213
216
 
214
- --layout-container-max-width: 80rem;
215
-
216
217
  --line-height-base: 1.5;
217
218
  --line-height-sm: 1.4;
218
219
  --line-height-xs: 1.25;
@@ -262,8 +263,8 @@
262
263
  --mpopup-header-font-size: var(--font-size-xl);
263
264
  --mpopup-header-padding: var(--size-xs);
264
265
  --mpopup-height: max-content;
265
- --mpopup-max-height: 5.625rem;
266
- --mpopup-max-width: 4.375rem;
266
+ --mpopup-max-height: 90dvh;
267
+ --mpopup-max-width: 70dvw;
267
268
  --mpopup-min-height: 18.75rem;
268
269
  --mpopup-min-width: 25rem;
269
270
  --mpopup-width: max-content;
@@ -1,5 +1,15 @@
1
1
  /*! minimo - Classnames */
2
- /** @typedef {string | null | undefined | false | 0} ClassValue */
2
+
3
+ /**
4
+ * `MinimoClass | any other string`: keeps editor autocomplete for minimo's
5
+ * own class names (see ../../types/classes.d.ts, auto-generated) while still
6
+ * accepting arbitrary consumer-defined class names. The `string & {}` branding
7
+ * is a TS trick that prevents the union from collapsing to plain `string`,
8
+ * which would silently disable the autocomplete.
9
+ * @typedef {import('../../types/classes.js').MinimoClass | (string & {})} ClassName
10
+ */
11
+
12
+ /** @typedef {ClassName | null | undefined | false | 0} ClassValue */
3
13
 
4
14
  /**
5
15
  * Joins CSS class names, filtering out falsy and non-string values.