@massimo-cassandro/minimo 1.36.1 → 1.37.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.
- package/dev-tools/design-tokens-builder/README.md +22 -0
- package/dev-tools/design-tokens-builder/build-tokens-src/config.mjs +22 -3
- package/dev-tools/design-tokens-builder/build-tokens-src/platforms.mjs +4 -2
- package/dev-tools/design-tokens-builder/build-tokens-src/source-prefixes.mjs +129 -0
- package/dev-tools/design-tokens-builder/build-tokens-src/transforms.mjs +20 -0
- package/dev-tools/design-tokens-builder/build-tokens.mjs +9 -0
- package/dev-tools/design-tokens-builder/check-unresolved-custom-props.mjs +13 -8
- package/dev-tools/design-tokens-builder/tokens-config-sample.mjs +10 -1
- package/dev-tools/starter-kit/source_files/app/email/_mail-properties.scss +18 -0
- package/dev-tools/starter-kit/source_files/app/email/mail-embed.scss +68 -0
- package/dev-tools/starter-kit/source_files/app/email/mail-inline.scss +203 -0
- package/dev-tools/starter-kit/source_files/app/email/readme.md +26 -0
- package/dev-tools/starter-kit/source_files/app/email/{_mail-properties.css → versione postcss/_mail-properties.css } +2 -5
- package/dev-tools/starter-kit/source_files/app/email/{mail-embed.css → versione postcss/mail-embed.css } +14 -15
- package/dev-tools/starter-kit/source_files/app/email/{mail-inline.css → versione postcss/mail-inline.css } +50 -61
- package/dev-tools/starter-kit/source_files/app/email/{postcss.config.mjs → versione postcss/postcss.config.mjs } +1 -1
- package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf/templates}/_main-tpl.html.twig +3 -0
- package/dev-tools/starter-kit/source_files/root/{sf_templates → _sf/templates}/email/_email-tpl.html.twig +8 -9
- package/dev-tools/starter-kit/source_files/root/_sf/templates/email/css/.gitkeep +0 -0
- package/dev-tools/starter-kit/source_files/root/_sf/templates/email/imgs/.gitkeep +0 -0
- package/dev-tools/starter-kit/source_files/root/jsconfig.json +1 -1
- package/dev-tools/starter-kit/source_files/root/package-tpl.json +5 -2
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/css-rules.mjs +1 -0
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/custom-props-purgecss-plugin.mjs +328 -0
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/get-jsConfig-aliases.mjs +1 -0
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/inline-critical-css.mjs +1 -0
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/mini-svg-data-uri-loader.cjs +1 -0
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/postcss.config.mjs +9 -5
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/purgecss-setup.mjs +23 -27
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/svg-rules.mjs +1 -0
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/svgo.config.mjs +1 -0
- package/dev-tools/starter-kit/source_files/root/webpack.config.mjs +137 -65
- package/dev-tools/starter-kit/starter-install.sh +2 -1
- package/dev-tools/starter-kit/webpack-config-upg.md +147 -0
- package/package.json +1 -1
- package/src/css/forms/form-group.css +0 -1
- package/src/minimo.css +42 -42
- package/dev-tools/starter-kit/source_files/root/sf_templates/email/css/mail-embed.css +0 -1
- package/dev-tools/starter-kit/source_files/root/sf_templates/email/css/mail-inline.css +0 -1
- package/dev-tools/starter-kit/source_files/root/webpack-config-modules/purgecss-variables-safelist.mjs +0 -92
- /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root}/_env.local +0 -0
- /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf}/symfony-webpack-setup.md +0 -0
- /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf/templates}/bundles/TwigBundle/Exception/error.html.twig +0 -0
- /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf/templates/incl}/flash-messages.html.twig +0 -0
- /package/{snippets/twig-symfony → dev-tools/starter-kit/source_files/root/_sf/templates/incl}/minimo-form-layout.html.twig +0 -0
- /package/snippets/{twig-symfony → symfony}/ImgViewerController.php +0 -0
- /package/snippets/{twig-symfony → symfony}/UnsplashController.php +0 -0
- /package/snippets/{twig-symfony → symfony}/ViewerController.php +0 -0
|
@@ -61,6 +61,7 @@ npx buildTokens --config ./path/to/tokens-config.mjs
|
|
|
61
61
|
The `source` array accepts any combination of:
|
|
62
62
|
- Concrete file paths: `'./tokens/colors.jsonc'`
|
|
63
63
|
- Glob patterns: `'./tokens/**/*.{json,jsonc,mjs}'`
|
|
64
|
+
- `{ src, prefix }` objects, to add a prefix to the custom properties of those files (see [Source prefixes](#source-prefixes))
|
|
64
65
|
|
|
65
66
|
**Supported formats:**
|
|
66
67
|
- `.json` / `.jsonc` — parsed directly as token data
|
|
@@ -70,6 +71,27 @@ The `source` array accepts any combination of:
|
|
|
70
71
|
|
|
71
72
|
Known limitation: a legacy node that is at the same time a token (own `value`/`type`) *and* a group with further nested children (e.g. Open Props' `other.ease.out`, which has its own value plus `out.1` ... `out.5`) can't be represented in DTCG v5 — a node with `$value` is always a leaf, Style Dictionary doesn't descend into its children. Only the node's own value is converted; the nested children are silently lost.
|
|
72
73
|
|
|
74
|
+
#### Source prefixes
|
|
75
|
+
|
|
76
|
+
An entry of `source` (or of a `sourceModes` mode) can be an object `{ src, prefix }` instead of a string: every custom property generated from the files matched by `src` (a path, a glob or an array of them) is rendered with the given prefix. Useful to avoid name collisions with third-party token sets, or to make their origin explicit:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
source: [
|
|
80
|
+
'./my-tokens/*.mjs',
|
|
81
|
+
{ src: `${node_modules_path}/open-props/open-props.style-dictionary-tokens.json`, prefix: 'op' },
|
|
82
|
+
],
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
With this config Open Props' `gray.0` becomes `--op-gray-0`. `prefix` accepts `'op'`, `'op-'` or `'--op-'` (all equivalent).
|
|
86
|
+
|
|
87
|
+
- Only the custom property **name** changes, not the token path: `{references}` keep working, and a token of another source referencing `{gray.0}` is rendered as `var(--op-gray-0)`.
|
|
88
|
+
- The JSON output (`jsonBuildPath`) is **not** prefixed: it keeps the original token tree.
|
|
89
|
+
- `customPropsGroups` match the prefixed name, so a group can use `prefixes: ['op']`.
|
|
90
|
+
- Giving two different prefixes to the same file fails the build with an error.
|
|
91
|
+
- `check-unresolved-custom-props.mjs` understands the same syntax. Note that `extraCustomPropsFiles` entries are plain CSS files and are never prefixed.
|
|
92
|
+
|
|
93
|
+
See `build-tokens-src/source-prefixes.mjs`.
|
|
94
|
+
|
|
73
95
|
|
|
74
96
|
#### Token file format (JS example)
|
|
75
97
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import * as path from 'node:path';
|
|
12
12
|
import { pathToFileURL } from 'node:url';
|
|
13
|
-
import {
|
|
13
|
+
import { splitSourceEntries } from './source-prefixes.mjs';
|
|
14
14
|
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
// Read the --config flag
|
|
@@ -126,14 +126,33 @@ export const customPropsGroups = Array.isArray(buildConfig.customPropsGroups)
|
|
|
126
126
|
// properties split — see build-tokens-src/build-source-modes.mjs. When set
|
|
127
127
|
// (buildConfig.sourceModes is a non-null object), `source` is ignored and
|
|
128
128
|
// each mode's own array of source patterns is resolved the same way.
|
|
129
|
+
//
|
|
130
|
+
// Entries of `source` / of each mode can also be `{ src, prefix }` objects,
|
|
131
|
+
// to render the custom properties of those files with a prefix (see
|
|
132
|
+
// source-prefixes.mjs). They are flattened here into plain patterns for
|
|
133
|
+
// Style Dictionary; the prefixed ones are also collected in
|
|
134
|
+
// `prefixedSources`, to be registered by build-tokens.mjs.
|
|
129
135
|
// ---------------------------------------------------------------------------
|
|
130
136
|
const sourceModesRaw = buildConfig.sourceModes ?? null;
|
|
131
137
|
|
|
138
|
+
/** @type {import('./source-prefixes.mjs').PrefixedSource[]} */
|
|
139
|
+
export const prefixedSources = [];
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {Parameters<typeof splitSourceEntries>[0]} entries
|
|
143
|
+
* @returns {string[]}
|
|
144
|
+
*/
|
|
145
|
+
const parseSourceEntries = (entries) => {
|
|
146
|
+
const { patterns, prefixed } = splitSourceEntries(entries, configDir);
|
|
147
|
+
prefixedSources.push(...prefixed);
|
|
148
|
+
return patterns;
|
|
149
|
+
};
|
|
150
|
+
|
|
132
151
|
export const sourceModes = sourceModesRaw
|
|
133
152
|
? Object.fromEntries(
|
|
134
153
|
Object.entries(sourceModesRaw).map(([mode, modeSource]) => [
|
|
135
154
|
mode,
|
|
136
|
-
|
|
155
|
+
parseSourceEntries(modeSource),
|
|
137
156
|
])
|
|
138
157
|
)
|
|
139
158
|
: null;
|
|
@@ -156,4 +175,4 @@ if (sourceModes && buildConfig.sourceModesBase && sourceModesBase !== buildConfi
|
|
|
156
175
|
);
|
|
157
176
|
}
|
|
158
177
|
|
|
159
|
-
export const source = sourceModes ? null :
|
|
178
|
+
export const source = sourceModes ? null : parseSourceEntries(buildConfig.source);
|
|
@@ -11,9 +11,10 @@ import { buildJsonFiles } from './formats/json.mjs';
|
|
|
11
11
|
// Transforms applied to the CSS platform.
|
|
12
12
|
// Exported so other scripts (e.g. check-unresolved-custom-props.mjs) can
|
|
13
13
|
// build a Style Dictionary instance with matching transformed token names.
|
|
14
|
+
// name/kebab-prefixed = name/kebab + per-source prefix (see source-prefixes.mjs).
|
|
14
15
|
export const CSS_TRANSFORMS = [
|
|
15
16
|
'attribute/cti',
|
|
16
|
-
'name/kebab',
|
|
17
|
+
'name/kebab-prefixed',
|
|
17
18
|
'time/seconds',
|
|
18
19
|
'asset/url',
|
|
19
20
|
'size/pxToRem-smart',
|
|
@@ -27,7 +28,8 @@ export const CSS_TRANSFORMS = [
|
|
|
27
28
|
// Transforms applied to the JSON platform.
|
|
28
29
|
// Kept minimal on purpose: consuming tools expect original values (e.g. "16px"
|
|
29
30
|
// not "1rem") and alias references ({...}) must be preserved to maintain
|
|
30
|
-
// token links.
|
|
31
|
+
// token links. Source prefixes are not applied here (plain name/kebab): the
|
|
32
|
+
// JSON output keeps the original token tree.
|
|
31
33
|
// Exported so build-source-modes.mjs can build a matching json platform per
|
|
32
34
|
// sourceModes mode.
|
|
33
35
|
export const JSON_TRANSFORMS = [
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// build-tokens-src/source-prefixes.mjs
|
|
2
|
+
// Supports per-source custom property prefixes: an entry of `source` (or of a
|
|
3
|
+
// `sourceModes` mode) can be an object `{ src, prefix }` instead of a plain
|
|
4
|
+
// string, e.g.
|
|
5
|
+
//
|
|
6
|
+
// { src: 'node_modules/open-props/open-props.style-dictionary-tokens.json', prefix: 'op' }
|
|
7
|
+
//
|
|
8
|
+
// so that every token defined by those files is rendered as `--op-<name>`.
|
|
9
|
+
//
|
|
10
|
+
// How it works: the prefix is applied to the token NAME only (see the
|
|
11
|
+
// name/kebab-prefixed transform in transforms.mjs), never to its path.
|
|
12
|
+
// {references} are resolved through the token path and rendered with the
|
|
13
|
+
// already-prefixed name, so a token of another source referencing e.g.
|
|
14
|
+
// {gray.5} becomes `var(--op-gray-5)` with no change to the token files. The
|
|
15
|
+
// JSON output keeps the original (unprefixed) tree structure.
|
|
16
|
+
//
|
|
17
|
+
// The name transform only knows the concrete file a token comes from
|
|
18
|
+
// (token.filePath), while `src` may contain glob patterns: registerSourcePrefixes()
|
|
19
|
+
// expands them (through Style Dictionary itself, so the result matches
|
|
20
|
+
// token.filePath exactly) into a file path -> prefix map.
|
|
21
|
+
|
|
22
|
+
import StyleDictionary from 'style-dictionary';
|
|
23
|
+
import { LEGACY_TOKENS_PARSER_NAME } from './legacy-tokens-parser.mjs';
|
|
24
|
+
import { resolveSourcePaths } from './resolve-source-paths.mjs';
|
|
25
|
+
import { collectConcreteFilePaths } from './formats/json.mjs';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {object} PrefixedSource
|
|
29
|
+
* @property {string[]} src Source paths/globs, already resolved (see resolveSourcePaths)
|
|
30
|
+
* @property {string} prefix Normalised prefix, without leading `--` or trailing `-`
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** @type {Map<string, string>} */
|
|
34
|
+
let prefixByFile = new Map();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Normalises a user-provided prefix: 'op', 'op-' and '--op-' all become 'op'.
|
|
38
|
+
* @param {unknown} prefix
|
|
39
|
+
* @returns {string}
|
|
40
|
+
* @throws {Error} if the result is not a valid custom property name segment
|
|
41
|
+
*/
|
|
42
|
+
export const normalizePrefix = (prefix) => {
|
|
43
|
+
const value = String(prefix).trim().replace(/^-+/, '').replace(/-+$/, '');
|
|
44
|
+
if (!/^[a-z_][\w-]*$/i.test(value)) {
|
|
45
|
+
throw new Error(`[build-tokens] config: invalid source prefix "${prefix}"`);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Splits a `source` (or `sourceModes[mode]`) array, whose entries can be
|
|
52
|
+
* strings or `{ src, prefix }` objects, into the flat list of patterns to pass
|
|
53
|
+
* to Style Dictionary and the list of the prefixed entries. Patterns are
|
|
54
|
+
* resolved relative to baseDir (see resolveSourcePaths).
|
|
55
|
+
* @param {(string|{src: string|string[], prefix?: string})[]} entries
|
|
56
|
+
* @param {string} baseDir
|
|
57
|
+
* @returns {{patterns: string[], prefixed: PrefixedSource[]}}
|
|
58
|
+
*/
|
|
59
|
+
export const splitSourceEntries = (entries, baseDir) => {
|
|
60
|
+
/** @type {string[]} */
|
|
61
|
+
const patterns = [];
|
|
62
|
+
/** @type {PrefixedSource[]} */
|
|
63
|
+
const prefixed = [];
|
|
64
|
+
|
|
65
|
+
for (const entry of entries ?? []) {
|
|
66
|
+
if (typeof entry === 'string') {
|
|
67
|
+
patterns.push(...resolveSourcePaths([entry], baseDir));
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const isObjectEntry = entry !== null && typeof entry === 'object'
|
|
72
|
+
&& (typeof entry.src === 'string' || Array.isArray(entry.src));
|
|
73
|
+
if (!isObjectEntry) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`[build-tokens] config: invalid source entry ${JSON.stringify(entry)} `
|
|
76
|
+
+ '(expected a string or an object { src, prefix })'
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const src = resolveSourcePaths([].concat(entry.src), baseDir);
|
|
81
|
+
patterns.push(...src);
|
|
82
|
+
|
|
83
|
+
if (entry.prefix !== undefined && entry.prefix !== null && entry.prefix !== '') {
|
|
84
|
+
prefixed.push({ src, prefix: normalizePrefix(entry.prefix) });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { patterns, prefixed };
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Expands the `src` patterns of every prefixed entry into concrete file paths
|
|
93
|
+
* and stores the file path -> prefix map used by getSourcePrefix().
|
|
94
|
+
* Must be called after registerLegacyTokensParser() and before any build.
|
|
95
|
+
* @param {PrefixedSource[]} prefixedSources
|
|
96
|
+
* @returns {Promise<void>}
|
|
97
|
+
* @throws {Error} if the same file is given two different prefixes
|
|
98
|
+
*/
|
|
99
|
+
export const registerSourcePrefixes = async (prefixedSources) => {
|
|
100
|
+
/** @type {Map<string, string>} */
|
|
101
|
+
const map = new Map();
|
|
102
|
+
|
|
103
|
+
for (const { src, prefix } of prefixedSources) {
|
|
104
|
+
const sd = new StyleDictionary({
|
|
105
|
+
source: src,
|
|
106
|
+
parsers: [LEGACY_TOKENS_PARSER_NAME],
|
|
107
|
+
log: { verbosity: 'silent' },
|
|
108
|
+
platforms: {},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
for (const filePath of await collectConcreteFilePaths(sd)) {
|
|
112
|
+
const existing = map.get(filePath);
|
|
113
|
+
if (existing !== undefined && existing !== prefix) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`[build-tokens] config: "${filePath}" has two different prefixes ("${existing}" and "${prefix}")`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
map.set(filePath, prefix);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
prefixByFile = map;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {string|undefined} filePath token.filePath
|
|
127
|
+
* @returns {string|undefined} the prefix registered for that file, if any
|
|
128
|
+
*/
|
|
129
|
+
export const getSourcePrefix = (filePath) => (filePath ? prefixByFile.get(filePath) : undefined);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Imported once by build-tokens.mjs before buildAllPlatforms() is called.
|
|
4
4
|
|
|
5
5
|
import StyleDictionary from 'style-dictionary';
|
|
6
|
+
import { getSourcePrefix } from './source-prefixes.mjs';
|
|
6
7
|
|
|
7
8
|
const BASE_FONT_SIZE = 16;
|
|
8
9
|
|
|
@@ -212,3 +213,22 @@ StyleDictionary.registerTransform({
|
|
|
212
213
|
filter: (token) => token.$type === 'typography' || token.type === 'typography',
|
|
213
214
|
transform: (token) => token.$value ?? token.value,
|
|
214
215
|
});
|
|
216
|
+
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// 7. Kebab-case name with per-source prefix
|
|
219
|
+
// Same as the built-in name/kebab, but prepends the prefix registered for the
|
|
220
|
+
// token's source file (`{ src, prefix }` entries of `source`/`sourceModes`, see
|
|
221
|
+
// source-prefixes.mjs), e.g. Open Props' gray.0 with prefix 'op' -> op-gray-0.
|
|
222
|
+
// Only the name changes: the token path is untouched, so {references} keep
|
|
223
|
+
// resolving and are rendered as var(--<prefixed-name>). Files without a
|
|
224
|
+
// prefix get exactly the built-in name/kebab result.
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
StyleDictionary.registerTransform({
|
|
227
|
+
name: 'name/kebab-prefixed',
|
|
228
|
+
type: 'name',
|
|
229
|
+
transform: (token, options) => {
|
|
230
|
+
const kebab = StyleDictionary.hooks.transforms['name/kebab'].transform;
|
|
231
|
+
const prefix = getSourcePrefix(token.filePath);
|
|
232
|
+
return kebab(prefix ? { ...token, path: [prefix, ...token.path] } : token, options);
|
|
233
|
+
},
|
|
234
|
+
});
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// build-tokens-src/platforms.mjs <- builds the platforms object (css + optional json)
|
|
15
15
|
// build-tokens-src/build-source-modes.mjs <- alternative build path used when sourceModes is set
|
|
16
16
|
// build-tokens-src/merge-css.mjs <- supports the mergeCustomProps option (flat and per-mode)
|
|
17
|
+
// build-tokens-src/source-prefixes.mjs <- supports `{ src, prefix }` entries in source/sourceModes
|
|
17
18
|
|
|
18
19
|
import StyleDictionary from 'style-dictionary';
|
|
19
20
|
import * as path from 'node:path';
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
source,
|
|
36
37
|
sourceModes,
|
|
37
38
|
sourceModesBase,
|
|
39
|
+
prefixedSources,
|
|
38
40
|
mergeCustomProps,
|
|
39
41
|
customPropsGroups,
|
|
40
42
|
pxToRem,
|
|
@@ -50,6 +52,13 @@ import './build-tokens-src/transforms.mjs';
|
|
|
50
52
|
import { LEGACY_TOKENS_PARSER_NAME, registerLegacyTokensParser } from './build-tokens-src/legacy-tokens-parser.mjs';
|
|
51
53
|
registerLegacyTokensParser();
|
|
52
54
|
|
|
55
|
+
// ── 2c. Per-source custom property prefixes ──────────────────────────────────
|
|
56
|
+
// Expands the `{ src, prefix }` entries of source/sourceModes into the file ->
|
|
57
|
+
// prefix map read by the name/kebab-prefixed transform (needs the legacy
|
|
58
|
+
// parser above, to load legacy .json sources) — see source-prefixes.mjs.
|
|
59
|
+
import { registerSourcePrefixes } from './build-tokens-src/source-prefixes.mjs';
|
|
60
|
+
await registerSourcePrefixes(prefixedSources);
|
|
61
|
+
|
|
53
62
|
// ── 3. CSS format ─────────────────────────────────────────────────────────────
|
|
54
63
|
// customPropsCount is updated by the format at render time
|
|
55
64
|
// loadExistingCustomProps supports the mergeCustomProps option (see below)
|
|
@@ -24,7 +24,7 @@ import { styleText } from 'node:util';
|
|
|
24
24
|
import StyleDictionary from 'style-dictionary';
|
|
25
25
|
import './build-tokens-src/transforms.mjs';
|
|
26
26
|
import { CSS_TRANSFORMS } from './build-tokens-src/platforms.mjs';
|
|
27
|
-
import {
|
|
27
|
+
import { splitSourceEntries, registerSourcePrefixes } from './build-tokens-src/source-prefixes.mjs';
|
|
28
28
|
import { findMediaModeBlocks } from './build-tokens-src/merge-css.mjs';
|
|
29
29
|
import { LEGACY_TOKENS_PARSER_NAME, registerLegacyTokensParser } from './build-tokens-src/legacy-tokens-parser.mjs';
|
|
30
30
|
|
|
@@ -202,17 +202,22 @@ async function run() {
|
|
|
202
202
|
/** @type {Map<string, Map<string, string>>} */
|
|
203
203
|
const sourceFileByPropByMode = new Map();
|
|
204
204
|
|
|
205
|
-
//
|
|
205
|
+
// includeEntries: base-mode sources, passed as `include` for non-base
|
|
206
206
|
// modes so cross-mode references resolve (same as build-source-modes.mjs).
|
|
207
|
-
/** @param {
|
|
208
|
-
|
|
207
|
+
/** @param {Parameters<typeof splitSourceEntries>[0]} sourceEntries @param {Parameters<typeof splitSourceEntries>[0]} [includeEntries] @returns {Promise<Map<string,string>>} */
|
|
208
|
+
// Entries can be strings or `{ src, prefix }` objects (see
|
|
209
|
+
// build-tokens-src/source-prefixes.mjs): the prefixes are registered
|
|
210
|
+
// before the transform pipeline runs, so token names match the CSS.
|
|
211
|
+
const resolveSourceFileMap = async (sourceEntries, includeEntries = []) => {
|
|
209
212
|
/** @type {Map<string, string>} */
|
|
210
213
|
const map = new Map();
|
|
211
|
-
const resolvedPaths =
|
|
214
|
+
const { patterns: resolvedPaths, prefixed } = splitSourceEntries(sourceEntries, configDir);
|
|
215
|
+
const { patterns: includePaths, prefixed: includePrefixed } = splitSourceEntries(includeEntries, configDir);
|
|
212
216
|
if (!resolvedPaths.length) return map;
|
|
213
217
|
try {
|
|
218
|
+
await registerSourcePrefixes([...prefixed, ...includePrefixed]);
|
|
214
219
|
const sd = new StyleDictionary({
|
|
215
|
-
include:
|
|
220
|
+
include: includePaths,
|
|
216
221
|
source: resolvedPaths,
|
|
217
222
|
parsers: [LEGACY_TOKENS_PARSER_NAME],
|
|
218
223
|
log: { verbosity: 'silent' },
|
|
@@ -230,8 +235,8 @@ async function run() {
|
|
|
230
235
|
|
|
231
236
|
if (checkUnused && config.sourceModes) {
|
|
232
237
|
for (const [mode, modeSource] of Object.entries(config.sourceModes)) {
|
|
233
|
-
const
|
|
234
|
-
sourceFileByPropByMode.set(mode, await resolveSourceFileMap(modeSource,
|
|
238
|
+
const includeEntries = mode === sourceModesBase ? [] : config.sourceModes[sourceModesBase];
|
|
239
|
+
sourceFileByPropByMode.set(mode, await resolveSourceFileMap(modeSource, includeEntries));
|
|
235
240
|
}
|
|
236
241
|
} else if (checkUnused && Array.isArray(config.source) && config.source.length) {
|
|
237
242
|
const map = await resolveSourceFileMap(config.source);
|
|
@@ -51,12 +51,21 @@ const config = {
|
|
|
51
51
|
// the pattern syntax. Use template literals instead:
|
|
52
52
|
// OK: `${minimo_path}/**/*.{json,mjs}`
|
|
53
53
|
// NO: path.join(minimo_path, '/**/*.{json,mjs}')
|
|
54
|
+
//
|
|
55
|
+
// An entry can also be an object `{ src, prefix }` (src: string or array of
|
|
56
|
+
// strings/globs), to render the custom properties of those files with a
|
|
57
|
+
// prefix: e.g. { src: '...open-props...json', prefix: 'op' } turns Open
|
|
58
|
+
// Props' gray.0 into --op-gray-0. Only the property name changes:
|
|
59
|
+
// {references} to those tokens are rendered as var(--op-gray-0), and the JSON
|
|
60
|
+
// output stays unprefixed. 'op', 'op-' and '--op-' are equivalent. The same
|
|
61
|
+
// syntax works inside sourceModes. Two different prefixes for the same file
|
|
62
|
+
// make the build fail.
|
|
54
63
|
source: [
|
|
55
64
|
`${minimo_path}/design-tokens/_src/**/*.tokens.{mjs,jsonc}`, // main tokens
|
|
56
65
|
`${minimo_path}/src/**/*.tokens.{mjs,jsonc}`, // components tokens
|
|
57
66
|
|
|
58
67
|
// optional extra token source (in this example, openProps, https://open-props.style/)
|
|
59
|
-
// `${node_modules_path}/open-props/open-props.style-dictionary-tokens.json`,
|
|
68
|
+
// { src: `${node_modules_path}/open-props/open-props.style-dictionary-tokens.json`, prefix: 'op' },
|
|
60
69
|
|
|
61
70
|
// your project tokens
|
|
62
71
|
'./project-tokens/*.{js,mjs,jsonc,json}',
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
$mail-font-size-xs: 13px;
|
|
2
|
+
$mail-font-size-lg: 18px;
|
|
3
|
+
$mail-font-size-2lg: 21px;
|
|
4
|
+
$mail-font-size-3lg: 24px;
|
|
5
|
+
$mail-font-size-xl: 28px;
|
|
6
|
+
|
|
7
|
+
$mail-body-font: 'Aptos', 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif;
|
|
8
|
+
$mail-font-monospace: 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
|
|
9
|
+
$mail-body-bg: #efefef;
|
|
10
|
+
$mail-text-color: #333;
|
|
11
|
+
$mail-text-color-light: #373737;
|
|
12
|
+
$mail-gray: #4b4a4a;
|
|
13
|
+
|
|
14
|
+
$mail-content-bg: #fff;
|
|
15
|
+
$mail-spacing: 24px;
|
|
16
|
+
|
|
17
|
+
$mail-alt-color: #036;
|
|
18
|
+
$mail-darken-alt-color: #010112;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* stylelint-disable declaration-no-important */
|
|
2
|
+
/* stylelint-disable property-no-vendor-prefix */
|
|
3
|
+
@use './mail-properties' as *;
|
|
4
|
+
|
|
5
|
+
/* NB: niente selettore universale `*`: inline_css lo inlinerebbe su ogni elemento */
|
|
6
|
+
body,
|
|
7
|
+
table,
|
|
8
|
+
td,
|
|
9
|
+
p,
|
|
10
|
+
a {
|
|
11
|
+
-webkit-text-size-adjust: 100%;
|
|
12
|
+
text-size-adjust: 100%;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
gli stili base dei link sono già in mail-inline;
|
|
17
|
+
qui serve !important perché lo style inline batte l'hover
|
|
18
|
+
*/
|
|
19
|
+
.content.body a:hover {
|
|
20
|
+
color: $mail-darken-alt-color !important;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@media only screen and (max-width: 640px) {
|
|
24
|
+
body {
|
|
25
|
+
&[class='yahoo-fix'] {
|
|
26
|
+
.device-width {
|
|
27
|
+
width: 440px !important;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.content {
|
|
33
|
+
&.columns-wrapper {
|
|
34
|
+
td.column-spacer {
|
|
35
|
+
height: 0 !important;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
td.column-width, td.column-spacer {
|
|
39
|
+
display: block !important;
|
|
40
|
+
width: 100% !important;
|
|
41
|
+
}
|
|
42
|
+
.column-content {
|
|
43
|
+
& td {
|
|
44
|
+
& h2 {
|
|
45
|
+
font-size: $mail-font-size-3lg !important;
|
|
46
|
+
}
|
|
47
|
+
& .text {
|
|
48
|
+
font-size: $mail-font-size-lg !important;
|
|
49
|
+
}
|
|
50
|
+
& .buttons {
|
|
51
|
+
font-size: $mail-font-size-2lg !important;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@media only screen and (max-width: 479px) {
|
|
60
|
+
body {
|
|
61
|
+
&[class='yahoo-fix'] {
|
|
62
|
+
.device-width {
|
|
63
|
+
width: 280px !important;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/* stylelint-disable max-nesting-depth */
|
|
2
|
+
/* stylelint-disable selector-max-compound-selectors */
|
|
3
|
+
/* stylelint-disable selector-max-id */
|
|
4
|
+
/* stylelint-disable property-no-unknown */
|
|
5
|
+
@use './mail-properties' as *;
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
width: 100%;
|
|
9
|
+
padding: 0;
|
|
10
|
+
margin: 0;
|
|
11
|
+
font-family: $mail-body-font;
|
|
12
|
+
font-style: normal;
|
|
13
|
+
font-weight: 400;
|
|
14
|
+
line-height: 1.5;
|
|
15
|
+
color: $mail-text-color;
|
|
16
|
+
background-color: $mail-body-bg;
|
|
17
|
+
-webkit-font-smoothing: antialiased;
|
|
18
|
+
|
|
19
|
+
.device-width {
|
|
20
|
+
width: 600px;
|
|
21
|
+
padding: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* .full-width {
|
|
25
|
+
width: 100%;
|
|
26
|
+
padding: 0;
|
|
27
|
+
} */
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/*
|
|
31
|
+
div.outerWrapper {
|
|
32
|
+
background-color: $mail-content-bg;
|
|
33
|
+
//border-radius:20px;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
margin: 0 auto;
|
|
36
|
+
}
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
table {
|
|
40
|
+
border-collapse: collapse;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
p {
|
|
44
|
+
mso-table-lspace: 0;
|
|
45
|
+
mso-table-rspace: 0;
|
|
46
|
+
margin-top: $mail-spacing * .5;
|
|
47
|
+
margin-bottom: $mail-spacing * .5;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#wrapper{
|
|
51
|
+
width: 100%;
|
|
52
|
+
margin: 0 auto;
|
|
53
|
+
border: none;
|
|
54
|
+
|
|
55
|
+
/* &.full-page{
|
|
56
|
+
width: 95%;
|
|
57
|
+
} */
|
|
58
|
+
|
|
59
|
+
> tbody > tr > td {
|
|
60
|
+
width: 100%;
|
|
61
|
+
padding-top: 10px;
|
|
62
|
+
padding-bottom: 20px;
|
|
63
|
+
background-color: $mail-body-bg;
|
|
64
|
+
border: none;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.content {
|
|
69
|
+
margin: 0 auto;
|
|
70
|
+
|
|
71
|
+
&.header {
|
|
72
|
+
> tbody > tr > td {
|
|
73
|
+
padding: 10px;
|
|
74
|
+
text-align: center;
|
|
75
|
+
background-color: $mail-body-bg;
|
|
76
|
+
|
|
77
|
+
> img {
|
|
78
|
+
display: inline-block;
|
|
79
|
+
max-width: 100%;
|
|
80
|
+
height: auto;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
.logo {
|
|
84
|
+
padding-top: 10px;
|
|
85
|
+
padding-bottom: 10px;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
&.body {
|
|
90
|
+
> tbody > tr > td {
|
|
91
|
+
|
|
92
|
+
padding-top: 20px;
|
|
93
|
+
padding-right: 30px;
|
|
94
|
+
padding-bottom: 20px;
|
|
95
|
+
padding-left: 30px;
|
|
96
|
+
font-size: $mail-font-size-lg;
|
|
97
|
+
vertical-align: top;
|
|
98
|
+
color: $mail-text-color;
|
|
99
|
+
background-color: $mail-content-bg;
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
a {
|
|
103
|
+
font-weight: 700;
|
|
104
|
+
color: $mail-alt-color;
|
|
105
|
+
text-decoration: underline;
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
h1 {
|
|
110
|
+
margin-top: 0;
|
|
111
|
+
margin-bottom: $mail-spacing;
|
|
112
|
+
font-size: $mail-font-size-xl;
|
|
113
|
+
font-weight: 700;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
h2 {
|
|
117
|
+
margin-top: $mail-spacing;
|
|
118
|
+
margin-bottom: 12px;
|
|
119
|
+
font-size: $mail-font-size-2lg;
|
|
120
|
+
font-weight: 700;
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
img {
|
|
125
|
+
max-width: 100%;
|
|
126
|
+
height: auto;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
&.footer {
|
|
134
|
+
> tbody > tr > td {
|
|
135
|
+
padding-top: 10px;
|
|
136
|
+
padding-bottom: 10px;
|
|
137
|
+
font-size: 80%;
|
|
138
|
+
color: $mail-text-color-light;
|
|
139
|
+
text-align: center;
|
|
140
|
+
background-color: $mail-body-bg;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
.text-center {
|
|
147
|
+
text-align: center;
|
|
148
|
+
}
|
|
149
|
+
.text-right {
|
|
150
|
+
text-align: right;
|
|
151
|
+
}
|
|
152
|
+
.text-left {
|
|
153
|
+
text-align: left;
|
|
154
|
+
}
|
|
155
|
+
.small, small {
|
|
156
|
+
font-size: $mail-font-size-xs;
|
|
157
|
+
}
|
|
158
|
+
.text-monospace {
|
|
159
|
+
font-family: $mail-font-monospace;
|
|
160
|
+
}
|
|
161
|
+
.text-muted {
|
|
162
|
+
color: #666;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
em, i {
|
|
167
|
+
font-style: italic;
|
|
168
|
+
}
|
|
169
|
+
strong, b {
|
|
170
|
+
font-weight: 700;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* body content table */
|
|
174
|
+
.table {
|
|
175
|
+
width: 100%;
|
|
176
|
+
margin: 12px 0 24px;
|
|
177
|
+
border-collapse: collapse;
|
|
178
|
+
|
|
179
|
+
th, td {
|
|
180
|
+
padding: 6px 10px;
|
|
181
|
+
font-size: $mail-font-size-lg;
|
|
182
|
+
vertical-align: top;
|
|
183
|
+
text-align: left;
|
|
184
|
+
border-bottom: 1px solid $mail-gray;
|
|
185
|
+
}
|
|
186
|
+
th {
|
|
187
|
+
font-weight: 600;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
thead {
|
|
191
|
+
th {
|
|
192
|
+
border-bottom-width: 2px;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* dopo .table, altrimenti il suo margin sovrascrive queste utility */
|
|
198
|
+
.margin-top {
|
|
199
|
+
margin-top: $mail-spacing;
|
|
200
|
+
}
|
|
201
|
+
.margin-bottom {
|
|
202
|
+
margin-bottom: $mail-spacing;
|
|
203
|
+
}
|