@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// webpack.config.mjs __project_name__
|
|
2
|
+
// v.3
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { fileURLToPath } from 'url';
|
|
4
5
|
import webpack from 'webpack';
|
|
@@ -41,6 +42,11 @@ const isDevelopment = process.env.NODE_ENV === 'development'
|
|
|
41
42
|
,devServerPort = [[port5700]]
|
|
42
43
|
,apiPort = [[port8000]] // eslint-disable-line no-unused-vars
|
|
43
44
|
,useSass = false
|
|
45
|
+
|
|
46
|
+
// css in dev: true = iniettato con style-loader (veloce, nessun asset css);
|
|
47
|
+
// false = estratto in asset con MiniCssExtractPlugin (come in produzione).
|
|
48
|
+
// NB: con `purgeCssInDev: true` (vedi sotto) viene comunque forzato a false,
|
|
49
|
+
// perché il purge e il plugin delle custom properties lavorano sugli asset css
|
|
44
50
|
,inlineCssInDevMode = true
|
|
45
51
|
// sf: inlineCssInDevMode = false
|
|
46
52
|
,useSvgo = true
|
|
@@ -76,36 +82,65 @@ const isDevelopment = process.env.NODE_ENV === 'development'
|
|
|
76
82
|
|
|
77
83
|
,usePurgeCss = true // false per disattivare PurgeCSS (debug rapido di problemi legati al purge)
|
|
78
84
|
,purgeCSSOptions = {
|
|
79
|
-
variables: false, // rimuove le custom properties non usate (sostituisce jit-props)
|
|
80
85
|
keyframes: true, // rimuove i @keyframes non referenziati
|
|
81
86
|
debug: false // true per loggare i selettori rimossi da PurgeCSS a fine build
|
|
82
87
|
}
|
|
88
|
+
|
|
89
|
+
// plugin per l'estrazione ottimizzata delle custom properties (vedi
|
|
90
|
+
// webpack-config-modules/custom-props-purgecss-plugin.mjs): dopo la
|
|
91
|
+
// minificazione inietta nei css compilati SOLO le definizioni di custom
|
|
92
|
+
// properties effettivamente usate, prese dal file master. I css del progetto
|
|
93
|
+
// contengono quindi solo usi `var(--nome)`, mai definizioni (custom-properties.css
|
|
94
|
+
// NON va importato nelle entry css). Se false il plugin non viene nemmeno
|
|
95
|
+
// caricato e la gestione delle custom properties resta a carico del progetto
|
|
96
|
+
,useCustomPropsPlugin = true
|
|
97
|
+
// file master con TUTTE le definizioni di custom properties disponibili
|
|
98
|
+
// (copiato da minimo da starter-install.sh, generato da `npm run 'build tokens'`)
|
|
99
|
+
,customPropsFile = path.resolve(__dirname, './app/css/custom-properties.css')
|
|
100
|
+
|
|
101
|
+
// PurgeCSS e plugin custom properties lavorano sugli asset css finali:
|
|
102
|
+
// in dev, con `false`, sono entrambi disattivati (build più veloce, css
|
|
103
|
+
// iniettato con style-loader; se `useCustomPropsPlugin` è true le definizioni
|
|
104
|
+
// vengono aggiunte per intero alle entry, vedi `entries`).
|
|
105
|
+
// Con `true` (a scopo di test) sono attivi come in produzione (secondo i
|
|
106
|
+
// rispettivi flag `usePurgeCss` / `useCustomPropsPlugin`) e `inlineCssInDevMode`
|
|
107
|
+
// viene forzato a false
|
|
108
|
+
,purgeCssInDev = false
|
|
109
|
+
,inlineCssInDev = inlineCssInDevMode && !purgeCssInDev // valore effettivo passato a cssRules
|
|
110
|
+
,runPurgeCss = usePurgeCss && (!isDevelopment || purgeCssInDev)
|
|
111
|
+
,runCustomPropsPlugin = useCustomPropsPlugin && (!isDevelopment || purgeCssInDev)
|
|
83
112
|
// ,manifest_shared_seed = {}
|
|
84
113
|
;
|
|
85
114
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
115
|
+
const { CustomPropsPurgeCssPlugin } = runCustomPropsPlugin
|
|
116
|
+
? await import('./webpack-config-modules/custom-props-purgecss-plugin.mjs')
|
|
117
|
+
: {};
|
|
118
|
+
|
|
119
|
+
// cacheGroup `shared` (splitChunks): raccoglie in `shared.js` / `shared.css` i
|
|
120
|
+
// moduli js e css provenienti da node_modules e dalle directory condivise del
|
|
121
|
+
// progetto (vedi `pathsRegexp` sotto), così non vengono duplicati in ogni entry.
|
|
122
|
+
//
|
|
123
|
+
// Dalla v3 anche i css finiscono nello `shared.css` (in v2 restavano sempre nel
|
|
124
|
+
// css della entry, duplicati, per due limiti di PurgeCSS che operava sul singolo
|
|
125
|
+
// asset con `variables: true`): le custom properties sono ora gestite dal plugin
|
|
126
|
+
// CustomPropsPurgeCssPlugin, che lavora sugli asset finali e le cui definizioni
|
|
127
|
+
// vengono iniettate nello `shared.css` (vedi `sets` nei `plugins`).
|
|
94
128
|
//
|
|
95
|
-
//
|
|
96
|
-
// css
|
|
97
|
-
//
|
|
98
|
-
//
|
|
129
|
+
// - le entry `.critical` sono escluse dal chunk condiviso (`chunks` sotto):
|
|
130
|
+
// il loro css è inline nei template e deve restare autosufficiente
|
|
131
|
+
// - `enforce: true` garantisce che `shared.js` e `shared.css` siano sempre
|
|
132
|
+
// generati (senza, splitChunks non crea il chunk se i moduli sono sotto la
|
|
133
|
+
// soglia `minSize`, e il link nel template punterebbe a un file inesistente)
|
|
134
|
+
// - i template (twig/altro) devono linkare (preload + link) sia `shared.css` che il
|
|
135
|
+
// css della propria entry, con `shared.css` PRIMA del css della entry per
|
|
136
|
+
// rispettare l'ordine della cascata (vedi _sf/templates/_main-tpl.html.twig)
|
|
99
137
|
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
// definite/consumate tra asset diversi (vedi purgecss-variables-safelist.mjs),
|
|
106
|
-
// e i template dovranno linkare esplicitamente lo shared.css generato
|
|
138
|
+
// NB: PurgeCSS elabora ogni asset css singolarmente, quindi i @keyframes
|
|
139
|
+
// definiti nello `shared.css` ma usati solo in un altro asset (es. un css di
|
|
140
|
+
// progetto che usa una animation di minimo) verrebbero rimossi: in tal caso
|
|
141
|
+
// aggiungerli a `safelist.keyframes` o impostare `keyframes: false` in
|
|
142
|
+
// `purgeCSSOptions`
|
|
107
143
|
const shared_chunk_paths = (module) => {
|
|
108
|
-
if (module.type === 'css/mini-extract') return false;
|
|
109
144
|
|
|
110
145
|
// path delle directory da utilizzare nel chunk `shared`
|
|
111
146
|
const sep = '[\\\\/]'; // stringa che produce [\\/] nel pattern
|
|
@@ -180,7 +215,7 @@ const CopyWebpackPluginPatterns = [
|
|
|
180
215
|
|
|
181
216
|
// =>> entries
|
|
182
217
|
// NB: percorsi dalla root del progetto
|
|
183
|
-
const
|
|
218
|
+
const projectEntries = {
|
|
184
219
|
'index': './app/index.js',
|
|
185
220
|
|
|
186
221
|
// css critici da includere inline nei template html: il suffisso `.critical` nel nome
|
|
@@ -190,6 +225,18 @@ const entries = {
|
|
|
190
225
|
|
|
191
226
|
};
|
|
192
227
|
|
|
228
|
+
// dev senza plugin custom properties (`purgeCssInDev: false`, css iniettato con
|
|
229
|
+
// style-loader, quindi nessun asset css in cui il plugin possa inserire le
|
|
230
|
+
// definizioni): il file master con TUTTE le custom properties viene aggiunto come
|
|
231
|
+
// primo modulo di ogni entry (escluse le `.critical`). Non serve nessuna
|
|
232
|
+
// modifica ai template
|
|
233
|
+
const entries = (useCustomPropsPlugin && isDevelopment && !purgeCssInDev)
|
|
234
|
+
? Object.fromEntries(Object.entries(projectEntries).map(([name, entry]) => [
|
|
235
|
+
name,
|
|
236
|
+
/\.critical/.test(name) ? entry : [customPropsFile, ...[entry].flat()]
|
|
237
|
+
]))
|
|
238
|
+
: projectEntries;
|
|
239
|
+
|
|
193
240
|
|
|
194
241
|
const config = {
|
|
195
242
|
mode: isDevelopment ? 'development' : 'production',
|
|
@@ -240,10 +287,11 @@ const config = {
|
|
|
240
287
|
optimization: {
|
|
241
288
|
minimize: !isDevelopment,
|
|
242
289
|
// di default true solo se minimize:true (quindi false in dev): esplicito qui perché
|
|
243
|
-
// PurgeCSSPlugin
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
290
|
+
// PurgeCSSPlugin e il plugin delle custom properties modificano gli asset css
|
|
291
|
+
// dopo il calcolo dell'hash "veloce" — senza questo, in dev con `purgeCssInDev`
|
|
292
|
+
// il contenthash non riflette le loro modifiche e il browser può servire dalla
|
|
293
|
+
// cache una versione stale pur avendo rigenerato la build
|
|
294
|
+
realContentHash: !isDevelopment || purgeCssInDev,
|
|
247
295
|
minimizer: [
|
|
248
296
|
new CssMinimizerPlugin(),
|
|
249
297
|
new TerserPlugin({
|
|
@@ -261,10 +309,11 @@ const config = {
|
|
|
261
309
|
|
|
262
310
|
shared: {
|
|
263
311
|
// vedi shared_chunk_paths sopra per la logica completa del test
|
|
264
|
-
// (path match + esclusione sempre dei css)
|
|
265
312
|
test: shared_chunk_paths,
|
|
266
313
|
name: 'shared',
|
|
267
|
-
|
|
314
|
+
enforce: true,
|
|
315
|
+
// le entry `.critical` restano autosufficienti (css inline nei template)
|
|
316
|
+
chunks: (chunk) => !/\.critical/.test(chunk.name)
|
|
268
317
|
}
|
|
269
318
|
}
|
|
270
319
|
},
|
|
@@ -359,7 +408,8 @@ const config = {
|
|
|
359
408
|
|
|
360
409
|
// =>> plugins: WebpackManifestPlugin
|
|
361
410
|
// new WebpackManifestPlugin({
|
|
362
|
-
// fileName: path.join(output_dir, 'manifest.json'),
|
|
411
|
+
// fileName: path.join(output_dir, 'manifest.json'),
|
|
412
|
+
// sf: fileName: 'manifest.json', // scrive in output.path
|
|
363
413
|
// // basePath: item.source_dirname
|
|
364
414
|
// // removeKeyHash: /(^(_assets\/(?!(fonts\/))))|((\?as_asset)$)/,
|
|
365
415
|
// removeKeyHash: true, // /([a-f0-9]{32}\.?)/gi, // /(\?as_asset)$/,
|
|
@@ -462,29 +512,27 @@ const config = {
|
|
|
462
512
|
exclude: /\.critical/ // chunk name
|
|
463
513
|
}),
|
|
464
514
|
|
|
465
|
-
// =>> plugins: PurgeCSSPlugin (
|
|
515
|
+
// =>> plugins: PurgeCSSPlugin (solo classi/keyframes, le custom properties sono
|
|
516
|
+
// gestite dal plugin successivo)
|
|
466
517
|
// https://github.com/FullHuman/purgecss/tree/main/packages/purgecss-webpack-plugin
|
|
467
518
|
// https://purgecss.com/configuration.html
|
|
468
519
|
// istanze e opzioni in webpack-config-modules/purgecss-setup.mjs (qui solo flag/dati progetto)
|
|
469
520
|
// `usePurgeCss: false` disattiva il purge per intero (utile per isolare rapidamente
|
|
470
|
-
// eventuali problemi legati ad esso); `purgeCSSOptions` (
|
|
521
|
+
// eventuali problemi legati ad esso); `purgeCSSOptions` (keyframes/debug,
|
|
471
522
|
// vedi sopra) si applica solo quando è attivo
|
|
472
|
-
// NB: il purge è attivo
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
...(
|
|
523
|
+
// NB: in dev il purge è attivo solo con `purgeCssInDev: true` (vedi sopra). In
|
|
524
|
+
// watch mode i template twig NON sono osservati da webpack: dopo aver
|
|
525
|
+
// aggiunto una classe solo in un twig occorre rilanciare la build (o toccare
|
|
526
|
+
// un file js/css) per aggiornare il purge
|
|
527
|
+
...(runPurgeCss
|
|
477
528
|
? createPurgeCSSPlugins({
|
|
478
529
|
|
|
479
530
|
// SOLO file che generano markup o classi (twig, php, js);
|
|
480
531
|
// i file di stile vengono comunque scartati dal plugin
|
|
481
532
|
contentGlobs: [
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
// path.resolve(__dirname, './templates/**/*.twig'),
|
|
486
|
-
// path.resolve(__dirname, './app/**/*.php'),
|
|
487
|
-
path.resolve(__dirname, './app/index.js'), // entry js di default (vedi starter-install.sh)
|
|
533
|
+
path.resolve(__dirname, './templates/**/*.twig'),
|
|
534
|
+
path.resolve(__dirname, './app/**/*.php'),
|
|
535
|
+
path.resolve(__dirname, './app/index.js'), // entry js di default
|
|
488
536
|
path.resolve(__dirname, './app/src/**/*.{js,mjs,jsx}'),
|
|
489
537
|
path.resolve(__dirname, './app/src/**/*.ejs'), // template html di webpack
|
|
490
538
|
path.resolve(__dirname, './app/error-pages/**/*.js'),
|
|
@@ -519,31 +567,55 @@ const config = {
|
|
|
519
567
|
]
|
|
520
568
|
},
|
|
521
569
|
|
|
522
|
-
// elenco generato: usi di var() nei blocchi `purgecss ignore` e nei css
|
|
523
|
-
// shadow-DOM/`?raw`, override cross-asset (seeds), con chiusura transitiva
|
|
524
|
-
// delle dipendenze (vedi webpack-config-modules/purgecss-variables-safelist.mjs)
|
|
525
|
-
variablesSafelist: {
|
|
526
|
-
declarationGlobs: [
|
|
527
|
-
path.resolve(__dirname, './app/index.css'), // entry css di default (vedi starter-install.sh)
|
|
528
|
-
path.resolve(__dirname, './app/index-critical.css'),
|
|
529
|
-
path.resolve(__dirname, './app/src/**/*.css'),
|
|
530
|
-
path.resolve(__dirname, './app/error-pages/**/*.css'),
|
|
531
|
-
`${minimo_path}/src/**/*.css`,
|
|
532
|
-
],
|
|
533
|
-
shadowGlobs: [
|
|
534
|
-
path.resolve(__dirname, './app/src/web-components/**/*.css'),
|
|
535
|
-
`${minimo_path}/src/web-components/**/*.css`,
|
|
536
|
-
],
|
|
537
|
-
seeds: [
|
|
538
|
-
// override di .login-group (login.css) consumati dai buttons minimo (css globale)
|
|
539
|
-
/^--btn-secondary-/
|
|
540
|
-
]
|
|
541
|
-
},
|
|
542
|
-
|
|
543
570
|
// fontFace resta false (default): i font sono referenziati solo tramite
|
|
544
571
|
// var(--font-family) e purgecss non risolve le custom properties nei valori
|
|
545
572
|
...purgeCSSOptions
|
|
546
573
|
})
|
|
574
|
+
: []),
|
|
575
|
+
|
|
576
|
+
// =>> plugins: CustomPropsPurgeCssPlugin (dopo PurgeCSS e dopo la minificazione)
|
|
577
|
+
// estrazione ottimizzata delle custom properties: vedi il commento in testa a
|
|
578
|
+
// webpack-config-modules/custom-props-purgecss-plugin.mjs e i flag
|
|
579
|
+
// `useCustomPropsPlugin` / `purgeCssInDev` sopra
|
|
580
|
+
...(runCustomPropsPlugin
|
|
581
|
+
? [new CustomPropsPurgeCssPlugin({
|
|
582
|
+
|
|
583
|
+
// file master con tutte le definizioni disponibili
|
|
584
|
+
definitionsFile: customPropsFile,
|
|
585
|
+
|
|
586
|
+
// set di asset css elaborati in modo indipendente: `sources` sono i nomi
|
|
587
|
+
// degli asset compilati (con l'hash nel nome: usare `*`, oppure una
|
|
588
|
+
// RegExp), `target` l'asset in cui iniettare le definizioni (se omesso
|
|
589
|
+
// è il primo asset del set; se non corrisponde ad alcun asset e non ha
|
|
590
|
+
// `*` viene emesso come nuovo asset, che va poi linkato nei template).
|
|
591
|
+
// NB: `*` corrisponde a qualsiasi carattere: 'layout.*.css' corrisponde
|
|
592
|
+
// anche a 'layout.critical.<hash>.css'
|
|
593
|
+
sets: [
|
|
594
|
+
// il critical css è inline nei template: deve restare autosufficiente
|
|
595
|
+
{ sources: ['layout.critical.*.css'] },
|
|
596
|
+
|
|
597
|
+
// shared.css è linkato in tutte le pagine: contiene le definizioni
|
|
598
|
+
// per tutti i css delle entry (le definizioni non sono duplicate
|
|
599
|
+
// negli altri asset). Ogni css delle entry va elencato qui
|
|
600
|
+
{ sources: ['shared.*.css', 'index.*.css'], target: 'shared.*.css' },
|
|
601
|
+
],
|
|
602
|
+
|
|
603
|
+
// css usati fuori dagli asset purgati (shadow DOM dei web components,
|
|
604
|
+
// import `?raw`): tutti i loro usi di var() contano per tutti i set
|
|
605
|
+
extraUsageGlobs: [
|
|
606
|
+
path.resolve(__dirname, './app/src/web-components/**/*.css'),
|
|
607
|
+
`${minimo_path}/src/web-components/**/*.css`,
|
|
608
|
+
],
|
|
609
|
+
|
|
610
|
+
// props da includere comunque in tutti i set (nomi esatti o RegExp),
|
|
611
|
+
// es. override consumati da un altro asset
|
|
612
|
+
// seeds: [/^--btn-secondary-/],
|
|
613
|
+
|
|
614
|
+
// selettore dei blocchi `:root` del master (default: ':root')
|
|
615
|
+
// selector: ':where(html)',
|
|
616
|
+
|
|
617
|
+
minify: !isDevelopment
|
|
618
|
+
})]
|
|
547
619
|
: [])
|
|
548
620
|
], // end plugins
|
|
549
621
|
|
|
@@ -562,7 +634,7 @@ const config = {
|
|
|
562
634
|
test: /\.jsx?$/,
|
|
563
635
|
oneOf: [
|
|
564
636
|
{
|
|
565
|
-
resourceQuery: /
|
|
637
|
+
resourceQuery: /as_(?:lib|asset)/, // utilizzo come libreria / asset
|
|
566
638
|
type: 'asset/resource',
|
|
567
639
|
generator: {
|
|
568
640
|
filename: 'libs/[name].[contenthash][ext]'
|
|
@@ -692,7 +764,7 @@ const config = {
|
|
|
692
764
|
},
|
|
693
765
|
|
|
694
766
|
// =>> rules: css / scss
|
|
695
|
-
...cssRules({isDevelopment: isDevelopment, useSass: useSass, inlineCssInDevMode:
|
|
767
|
+
...cssRules({isDevelopment: isDevelopment, useSass: useSass, inlineCssInDevMode: inlineCssInDev, postcssConfig_path: postcssConfig_path})
|
|
696
768
|
] // end rules
|
|
697
769
|
}, // end module
|
|
698
770
|
|
|
@@ -77,7 +77,8 @@ optionalPackages=(
|
|
|
77
77
|
"simple-datatables | | richiesto da s-datatable-component"
|
|
78
78
|
"style-dictionary | dev | richiesto da dev-tools/design-tokens-builder (build-tokens)"
|
|
79
79
|
"@cybozu/eslint-config | dev | utilizzato da eslint per il check dei file css"
|
|
80
|
-
"
|
|
80
|
+
"sass | dev | per email (scss)"
|
|
81
|
+
"postcss postcss-cli postcss-import postcss-nested postcss-custom-properties autoprefixer cssnano | dev | per email (postcss)"
|
|
81
82
|
)
|
|
82
83
|
|
|
83
84
|
# Rimuove spazi iniziali/finali da una stringa (usata per i campi estratti
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Aggiornamento della configurazione webpack
|
|
2
|
+
|
|
3
|
+
La versione della configurazione in uso è indicata dal commento `// v.N` alla riga 2 di `webpack.config.mjs` e di ogni file in `webpack-config-modules/`.
|
|
4
|
+
|
|
5
|
+
Le versioni precedenti sono conservate in `archived/webpack-config-v2/` del repository di minimo.
|
|
6
|
+
|
|
7
|
+
> **Alternativa rapida:** rilanciare `starter-install.sh` (è idempotente): i file diversi dai sorgenti vengono rinominati `OLD-<nome>` e sostituiti dalla nuova versione. Poi si riportano nel nuovo `webpack.config.mjs` le personalizzazioni del progetto (entry, glob dei contenuti, safelist, template html, regole custom) copiandole da `OLD-webpack.config.mjs`. Le istruzioni seguenti servono per aggiornare a mano un progetto già esistente.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Da v2 a v3
|
|
12
|
+
|
|
13
|
+
Novità della v3: le definizioni delle custom properties non sono più gestite da PurgeCSS (`variables`) né da postcss-jit-props, ma da un plugin webpack dedicato, `custom-props-purgecss-plugin.mjs`, che opera sugli asset CSS finali (dopo la minificazione) e inietta solo le custom properties effettivamente usate (dipendenze transitive incluse), partendo dal file master `app/css/custom-properties.css`. Il plugin è attivabile con un flag, indipendente da `usePurgeCss`.
|
|
14
|
+
|
|
15
|
+
Conseguenza: i css non devono più restare autosufficienti (con le definizioni ripetute in ogni asset), quindi **anche i css condivisi finiscono nel chunk `shared`** (`shared.css`) invece di essere duplicati nel css di ogni entry. Le entry `.critical` restano autosufficienti. I template devono linkare `shared.css`.
|
|
16
|
+
|
|
17
|
+
Riepilogo delle modifiche:
|
|
18
|
+
|
|
19
|
+
| Cosa | Dove |
|
|
20
|
+
|---|---|
|
|
21
|
+
| Nuovo plugin custom properties (`useCustomPropsPlugin`) | `webpack-config-modules/custom-props-purgecss-plugin.mjs`, `webpack.config.mjs` |
|
|
22
|
+
| Flag `purgeCssInDev` (purge e plugin in dev on-demand) | `webpack.config.mjs` |
|
|
23
|
+
| PurgeCSS senza `variables` / `variablesSafelist` | `purgecss-setup.mjs`, `webpack.config.mjs` |
|
|
24
|
+
| `shared.css` generato (css condivisi non più duplicati) | `webpack.config.mjs` (cacheGroup `shared`) |
|
|
25
|
+
| Link a `shared.css` nei template | `_sf/templates/_main-tpl.html.twig` e template del progetto |
|
|
26
|
+
|
|
27
|
+
### 1. Moduli in `webpack-config-modules/`
|
|
28
|
+
|
|
29
|
+
- **Aggiungere** `custom-props-purgecss-plugin.mjs`.
|
|
30
|
+
- **Sostituire** `purgecss-setup.mjs` con la versione v3 (non gestisce più `variables` né `variablesSafelist`; la firma di `createPurgeCSSPlugins` non accetta più `variablesSafelist` e `variables`).
|
|
31
|
+
- **Eliminare** `purgecss-variables-safelist.mjs` (sostituito dal plugin).
|
|
32
|
+
- **Aggiornare** `postcss.config.mjs` (cambiano solo i commenti sulla gestione delle custom properties; il comportamento è invariato).
|
|
33
|
+
- Negli altri file (`css-rules.mjs`, `get-jsConfig-aliases.mjs`, `inline-critical-css.mjs`, `mini-svg-data-uri-loader.cjs`, `svg-rules.mjs`, `svgo.config.mjs`) **cambia solo** la riga `// v.2` in `// v.3`. Il contenuto è identico alla v2 e possono essere lasciati invariati.
|
|
34
|
+
|
|
35
|
+
Non servono nuovi pacchetti npm: il plugin usa `postcss` e `glob`, già presenti tra le devDependencies dello starter. Se `postcss-jit-props` non è usato per altro, può essere disinstallato.
|
|
36
|
+
|
|
37
|
+
### 2. Modifiche a `webpack.config.mjs`
|
|
38
|
+
|
|
39
|
+
1. Riga 2: `// v.2` → `// v.3`.
|
|
40
|
+
2. Nel blocco delle costanti di configurazione, dopo `usePurgeCss`, **rimuovere** `variables` da `purgeCSSOptions`:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
,purgeCSSOptions = {
|
|
44
|
+
keyframes: true,
|
|
45
|
+
debug: false
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
3. **Aggiungere** i nuovi flag e le costanti derivate (dopo `purgeCSSOptions`):
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
,useCustomPropsPlugin = true // false: il plugin non viene caricato, le custom properties restano a carico del progetto
|
|
53
|
+
,customPropsFile = path.resolve(__dirname, './app/css/custom-properties.css')
|
|
54
|
+
,purgeCssInDev = false // true (solo per test): purge e plugin attivi anche in dev
|
|
55
|
+
,inlineCssInDev = inlineCssInDevMode && !purgeCssInDev
|
|
56
|
+
,runPurgeCss = usePurgeCss && (!isDevelopment || purgeCssInDev)
|
|
57
|
+
,runCustomPropsPlugin = useCustomPropsPlugin && (!isDevelopment || purgeCssInDev)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
4. **Aggiungere**, subito dopo la chiusura del blocco `const`, l'import dinamico del plugin:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
const { CustomPropsPurgeCssPlugin } = runCustomPropsPlugin
|
|
64
|
+
? await import('./webpack-config-modules/custom-props-purgecss-plugin.mjs')
|
|
65
|
+
: {};
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
5. **Entry**: rinominare l'oggetto `entries` in `projectEntries` e aggiungere subito sotto il nuovo `entries` derivato (in dev senza plugin, il master con tutte le custom properties viene aggiunto come primo modulo delle entry non `.critical`):
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
const entries = (useCustomPropsPlugin && isDevelopment && !purgeCssInDev)
|
|
72
|
+
? Object.fromEntries(Object.entries(projectEntries).map(([name, entry]) => [
|
|
73
|
+
name,
|
|
74
|
+
/\.critical/.test(name) ? entry : [customPropsFile, ...[entry].flat()]
|
|
75
|
+
]))
|
|
76
|
+
: projectEntries;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
6. **`optimization`**: sostituire il commento `// realContentHash: true,` con `realContentHash: !isDevelopment || purgeCssInDev,`.
|
|
80
|
+
7. **PurgeCSS**: la condizione `...(usePurgeCss ? createPurgeCSSPlugins({...}) : [])` diventa `...(runPurgeCss ? ... : [])`, e nella chiamata a `createPurgeCSSPlugins` va **rimosso** il blocco `variablesSafelist: { declarationGlobs, shadowGlobs, seeds }`. I glob `shadowGlobs` e `seeds` si spostano nel plugin (punto seguente, come `extraUsageGlobs` e `seeds`).
|
|
81
|
+
8. **Chunk `shared` anche per i css** (in v2 i css restavano nel css di ogni entry, duplicati). Nel commento e nella funzione `shared_chunk_paths` **rimuovere** la riga `if (module.type === 'css/mini-extract') return false;` (e il vecchio commento/`TODO` che spiegava perché i css restavano fuori dallo `shared`), e nel cacheGroup `shared` (`optimization.splitChunks.cacheGroups`) sostituire `chunks: 'all'` con:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
enforce: true, // shared.js e shared.css sempre generati, anche sotto la soglia minSize
|
|
85
|
+
chunks: (chunk) => !/\.critical/.test(chunk.name) // le entry `.critical` restano autosufficienti
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Ora viene generato anche `shared.css`, che va linkato nei template (vedi la sezione 3, "Template") e le cui definizioni delle custom properties vengono iniettate dal plugin (punto successivo, `sets`). Il purge dei `@keyframes` lavora sul singolo asset: se un `@keyframes` definito in `shared.css` è usato solo in un altro css, va aggiunto a `safelist.keyframes` (oppure `keyframes: false` in `purgeCSSOptions`).
|
|
89
|
+
9. **Aggiungere in fondo all'array `plugins`** (dopo PurgeCSS, che deve restare prima):
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
...(runCustomPropsPlugin
|
|
93
|
+
? [new CustomPropsPurgeCssPlugin({
|
|
94
|
+
definitionsFile: customPropsFile,
|
|
95
|
+
sets: [
|
|
96
|
+
// il critical css è inline nei template: deve restare autosufficiente
|
|
97
|
+
{ sources: ['layout.critical.*.css'] },
|
|
98
|
+
// shared.css è linkato in tutte le pagine e contiene le definizioni per tutti i css delle entry
|
|
99
|
+
{ sources: ['shared.*.css', 'index.*.css'], target: 'shared.*.css' },
|
|
100
|
+
],
|
|
101
|
+
extraUsageGlobs: [ // ex `shadowGlobs`
|
|
102
|
+
path.resolve(__dirname, './app/src/web-components/**/*.css'),
|
|
103
|
+
`${minimo_path}/src/web-components/**/*.css`,
|
|
104
|
+
],
|
|
105
|
+
// seeds: [/^--btn-secondary-/], // ex `seeds` di variablesSafelist, se ancora necessari
|
|
106
|
+
minify: !isDevelopment
|
|
107
|
+
})]
|
|
108
|
+
: [])
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
I `sets` vanno adattati alle entry del progetto: `sources` è l'elenco dei nomi degli asset CSS compilati (con l'hash nel nome, si usa `*`, oppure una RegExp) e `target` (opzionale) l'asset in cui iniettare le definizioni; se omesso è il primo asset del set. Ogni entry con un proprio css deve comparire in un set, altrimenti l'uso di custom properties in quel css non viene rilevato e le definizioni corrispondenti mancano.
|
|
112
|
+
10. **`cssRules`**: passare `inlineCssInDevMode: inlineCssInDev` (non più `inlineCssInDevMode`).
|
|
113
|
+
11. Aggiornare, se presenti, i commenti che citano `variables: true`, `safelist.variables` o `purgecss-variables-safelist.mjs`.
|
|
114
|
+
|
|
115
|
+
### 3. Template
|
|
116
|
+
|
|
117
|
+
Nel template di riferimento dello starter (`_sf/templates/_main-tpl.html.twig`) preload e link a `shared.css` sono già presenti. Nei template del progetto (twig/altro) che linkano gli asset a mano aggiungere preload e link a `shared.css`, **prima** di quelli del css della entry (vedi `_sf/templates/_main-tpl.html.twig`):
|
|
118
|
+
|
|
119
|
+
```twig
|
|
120
|
+
<link rel="preload" href="{{ asset('shared.css') }}" as="style">
|
|
121
|
+
<link rel="preload" href="{{ asset('index.css') }}" as="style">
|
|
122
|
+
<link rel="stylesheet" href="{{ asset('shared.css') }}" type="text/css" media="all">
|
|
123
|
+
<link rel="stylesheet" href="{{ asset('index.css') }}" type="text/css" media="all">
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Con `HtmlWebpackPlugin` (`inject`) `shared.css` è inserito automaticamente. Se il progetto ha entry aggiuntive con un proprio css (es. `admin`), aggiungere `shared.css` anche ai loro template e il css della entry ai `sets` del plugin.
|
|
127
|
+
|
|
128
|
+
### 4. File CSS del progetto
|
|
129
|
+
|
|
130
|
+
- Verificare che `app/css/custom-properties.css` esista (è il master, copiato da `starter-install.sh` da minimo). Non importarlo nelle entry CSS (in `app/css/index.css` l'`@import` deve restare commentato o assente): i css compilati devono contenere solo usi `var(--nome)`, altrimenti le definizioni sarebbero duplicate.
|
|
131
|
+
- Se `postcss.config.mjs` usa `postcss-jit-props`, eliminarlo (o lasciarlo commentato): non deve essere usato insieme al plugin.
|
|
132
|
+
|
|
133
|
+
### 5. Cambiamenti di comportamento
|
|
134
|
+
|
|
135
|
+
- **`shared.css`:** con `enforce: true` viene sempre generato (se esiste almeno un modulo css in `node_modules` o nelle directory di `shared_chunk_paths`). I template devono linkarlo prima del css della entry. In v2 non esisteva e nessun template lo linkava.
|
|
136
|
+
- **Dev con css iniettato da style-loader** (`inlineCssInDevMode = true`, il default dello starter): non viene generato nessun asset css, quindi neanche `shared.css`, e un `link` a `shared.css` nel template darebbe 404. Per i progetti con template server-side (es. Symfony) impostare `inlineCssInDevMode = false` (già previsto nei commenti `sf:` della config); con `HtmlWebpackPlugin` il link è inserito solo se il file esiste.
|
|
137
|
+
- **Dev:** con `purgeCssInDev: false` (default) né PurgeCSS né il plugin girano in dev, e il css resta iniettato con style-loader. In v2 il purge era sempre attivo anche in dev. Per verificare il purge o il plugin in dev impostare `purgeCssInDev = true`: `inlineCssInDevMode` viene forzato a `false` e `realContentHash` attivato.
|
|
138
|
+
- **Prod:** PurgeCSS non rimuove più le custom properties (`variables` è sempre `false`); le definizioni sono iniettate all'inizio del css target di ogni set, dopo il banner webpack, in ordine di dichiarazione del master. Le props con prefisso `--_` sono considerate locali e ignorate.
|
|
139
|
+
- Se un target non corrisponde ad alcun asset viene emesso un warning di webpack. Un target con nome letterale (senza `*`) che non corrisponde ad alcun asset viene invece emesso come nuovo asset, che va poi linkato nei template.
|
|
140
|
+
|
|
141
|
+
### 6. Verifica dopo l'aggiornamento
|
|
142
|
+
|
|
143
|
+
1. `NODE_ENV=production npx webpack --config ./webpack.config.mjs --stats minimal`: nessun errore o warning del plugin.
|
|
144
|
+
2. Nei css di output (`build/`) le custom properties usate sono definite nell'asset target del set (dopo il banner), quelle non usate sono assenti, e non compaiono duplicazioni tra critical e css principale.
|
|
145
|
+
3. In `build/` esiste `shared.<hash>.css` (con le definizioni delle custom properties), le pagine lo linkano prima del css della entry senza 404, i css delle entry non duplicano gli stili condivisi e la entry `.critical` resta autosufficiente.
|
|
146
|
+
4. `NODE_ENV=development` (con `webpack serve`): le pagine mostrano stili e colori corretti; il master viene caricato per intero come primo modulo delle entry.
|
|
147
|
+
5. Se qualche componente perde stili solo in produzione, provare `useCustomPropsPlugin = false` per isolare il problema.
|
package/package.json
CHANGED
package/src/minimo.css
CHANGED
|
@@ -5,53 +5,53 @@
|
|
|
5
5
|
/* @import '@css/custom-media.css'; */ /* managed by postcss */
|
|
6
6
|
/* @import '@css/custom-properties.css' layer('base'); */ /* managed by postcss-jit-props */
|
|
7
7
|
|
|
8
|
-
@import '@
|
|
9
|
-
@import '@
|
|
10
|
-
@import '@
|
|
11
|
-
|
|
12
|
-
@import '@
|
|
13
|
-
@import '@
|
|
14
|
-
@import '@
|
|
15
|
-
@import '@
|
|
16
|
-
@import '@
|
|
17
|
-
@import '@
|
|
18
|
-
@import '@
|
|
19
|
-
@import '@
|
|
20
|
-
@import '@
|
|
21
|
-
|
|
22
|
-
@import '@
|
|
23
|
-
@import '@
|
|
24
|
-
@import '@
|
|
25
|
-
@import '@
|
|
26
|
-
@import '@
|
|
27
|
-
|
|
28
|
-
@import '@
|
|
29
|
-
@import '@
|
|
30
|
-
@import '@
|
|
31
|
-
@import '@
|
|
32
|
-
@import '@
|
|
33
|
-
@import '@
|
|
34
|
-
@import '@
|
|
35
|
-
@import '@
|
|
36
|
-
@import '@
|
|
37
|
-
@import '@
|
|
38
|
-
@import '@
|
|
39
|
-
|
|
40
|
-
@import '@
|
|
41
|
-
@import '@
|
|
42
|
-
@import '@
|
|
43
|
-
/* @import '@
|
|
8
|
+
@import '@minimoSrc/css/reset.css';
|
|
9
|
+
@import '@minimoSrc/css/layout-base.css';
|
|
10
|
+
@import '@minimoSrc/css/container.css';
|
|
11
|
+
|
|
12
|
+
@import '@minimoSrc/css/forms/forms.css';
|
|
13
|
+
@import '@minimoSrc/css/forms/fieldset.css';
|
|
14
|
+
@import '@minimoSrc/css/forms/form-collections.css';
|
|
15
|
+
@import '@minimoSrc/css/forms/form-group.css';
|
|
16
|
+
@import '@minimoSrc/css/forms/input-group.css';
|
|
17
|
+
@import '@minimoSrc/css/forms/form-disabled-readonly.css';
|
|
18
|
+
@import '@minimoSrc/css/forms/validation.css';
|
|
19
|
+
@import '@minimoSrc/css/forms/form-utilities.css';
|
|
20
|
+
@import '@minimoSrc/css/forms/switch.css';
|
|
21
|
+
|
|
22
|
+
@import '@minimoSrc/css/buttons/buttons.css';
|
|
23
|
+
@import '@minimoSrc/css/buttons/buttons-primary.css';
|
|
24
|
+
@import '@minimoSrc/css/buttons/buttons-secondary.css';
|
|
25
|
+
@import '@minimoSrc/css/buttons/status-buttons.css';
|
|
26
|
+
@import '@minimoSrc/css/buttons/btn-close.css';
|
|
27
|
+
|
|
28
|
+
@import '@minimoSrc/css/badge.css';
|
|
29
|
+
@import '@minimoSrc/css/text.css';
|
|
30
|
+
@import '@minimoSrc/css/links.css';
|
|
31
|
+
@import '@minimoSrc/css/headings.css';
|
|
32
|
+
@import '@minimoSrc/css/alerts.css';
|
|
33
|
+
@import '@minimoSrc/css/icons.css';
|
|
34
|
+
@import '@minimoSrc/css/svg.css';
|
|
35
|
+
@import '@minimoSrc/css/table.css';
|
|
36
|
+
@import '@minimoSrc/css/flex.css';
|
|
37
|
+
@import '@minimoSrc/css/grid.css';
|
|
38
|
+
@import '@minimoSrc/css/details.css';
|
|
39
|
+
|
|
40
|
+
@import '@minimoSrc/css/sizes.css';
|
|
41
|
+
@import '@minimoSrc/css/display.css';
|
|
42
|
+
@import '@minimoSrc/css/margin-padding.css';
|
|
43
|
+
/* @import '@minimoSrc/css/margin-padding-media.css'; responsive version */
|
|
44
44
|
/* alternative boostrap naming
|
|
45
|
-
@import '@
|
|
46
|
-
@import '@
|
|
45
|
+
@import '@minimoSrc/css/margin-padding-bootstrap.css';
|
|
46
|
+
@import '@minimoSrc/css/margin-padding-bootsrap-media.css';
|
|
47
47
|
*/
|
|
48
|
-
@import '@
|
|
49
|
-
@import '@
|
|
50
|
-
@import '@
|
|
48
|
+
@import '@minimoSrc/css/overflow.css';
|
|
49
|
+
@import '@minimoSrc/css/position.css';
|
|
50
|
+
@import '@minimoSrc/css/visibility.css';
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
/* spinner: include the css of the choosen spinner only */
|
|
54
|
-
@import '@
|
|
54
|
+
@import '@minimoSrc/components/spinner/spinner-circle-basic.css';
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
/* @import '@css/layout.css'; */
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
a,body, p, table, td{-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%}.content.body a{font-weight:700;color:#036;text-decoration:underline;cursor:pointer}.content.body a:hover{color:#010112}@media only screen and (max-width:640px){body[class="yahoo-fix"] .device-width{width:440px!important}.content.columns-wrapper td.column-spacer{height:0!important} .content.columns-wrapper td.column-spacer,.content.columns-wrapper td.column-width{display:block!important;width:100%!important}.content.columns-wrapper .column-content td h2{font-size:24px!important}.content.columns-wrapper .column-content td .text{font-size:18px!important}.content.columns-wrapper .column-content td .buttons{font-size:21px!important}}@media only screen and (max-width:479px){body[class="yahoo-fix"] .device-width{width:280px!important}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
body{width:100%;padding:0;margin:0;font-family:helvetica,arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;color:#333;background-color:#efefef;-webkit-font-smoothing:antialiased}body .device-width{width:600px;padding:0}table{border-collapse:collapse}p{mso-table-lspace:0;mso-table-rspace:0;margin-top:12px;margin-bottom:12px}#wrapper{width:100%;margin:0 auto;border:none}#wrapper > tbody > tr > td{width:100%;padding:10px 10px 20px;background-color:#efefef;border:none}.content{margin:0 auto}.content.header > tbody > tr > td{padding:10px;text-align:center;background:#efefef}.content.header > tbody > tr > td > img{display:inline-block;max-width:100%;height:auto}.content.header .logo{padding-top:10px;padding-bottom:10px}.content.body > tbody > tr > td{padding:20px 30px;vertical-align:top;color:#333;background-color:#fff}.content.body > tbody > tr > td a{font-weight:700;color:#036;text-decoration:underline;cursor:pointer}.content.body > tbody > tr > td a:hover{color:#010112}.content.body > tbody > tr > td h1{margin-top:0;margin-bottom:24px;font-size:28px;font-weight:700}.content.body > tbody > tr > td p{font-size:21px}.content.body > tbody > tr > td img{max-width:100%;height:auto}.content.footer td{padding-top:10px;padding-bottom:10px;font-size:80%;color:#373737;text-align:center;background:#efefef}.logo-network{display:inline-block;padding:5px;margin:10px auto;background-color:#efefef}.network-info{font-size:13px;font-style:italic;color:#888}.text-center{text-align:center}.text-right{text-align:right}.text-left{text-align:left}.small, small{font-size:13px}.margin-top{margin-top:24px}.margin-bottom{margin-bottom:24px}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-muted{color:#666}em, i{font-style:italic} b,strong{font-weight:700}.table{width:100%;margin:12px 0 24px;border-collapse:collapse} .table td,.table th{padding:6px 10px;vertical-align:top;text-align:left;border-bottom:1px solid #4b4a4a}.table th{font-weight:600}.table thead th{border-bottom-width:2px}
|