css-is-awesome 1.11.0 → 1.12.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/CHANGELOG.md +41 -0
- package/CONTRACT.md +15 -0
- package/README.md +9 -8
- package/THEMING.md +1 -0
- package/bin/analyze.cjs +88 -1
- package/bin/cia.cjs +30 -5
- package/bin/migrate-chakra.cjs +249 -0
- package/bin/migrate-mui.cjs +363 -0
- package/bin/migrate-tailwind.cjs +3 -1
- package/dist/css-is-awesome.css +516 -0
- package/dist/css-is-awesome.min.css +1 -1
- package/dist/css-is-awesome.utilities.css +516 -0
- package/dist/css-is-awesome.utilities.min.css +1 -1
- package/dist/tokens.d.ts +4 -2
- package/package.json +7 -3
- package/public/theme.css +293 -237
- package/public/themes/boilerplate/theme.css +11 -10
- package/public/themes/boilerplate-dark/theme.css +10 -9
- package/public/themes/boilerplate-light/theme.css +10 -9
- package/public/themes/cupertino/theme.css +11 -10
- package/public/themes/cupertino-dark/theme.css +11 -10
- package/public/themes/cupertino-light/theme.css +11 -10
- package/public/themes/glass/theme.css +11 -10
- package/public/themes/glass-dark/theme.css +11 -10
- package/public/themes/glass-light/theme.css +11 -10
- package/public/themes/graphite/theme.css +11 -10
- package/public/themes/graphite-dark/theme.css +11 -10
- package/public/themes/graphite-light/theme.css +11 -10
- package/public/themes/press/theme.css +11 -10
- package/public/themes/press-dark/theme.css +11 -10
- package/public/themes/press-light/theme.css +11 -10
- package/public/themes/prism/theme.css +11 -10
- package/public/themes/prism-dark/theme.css +11 -10
- package/public/themes/prism-light/theme.css +11 -10
- package/public/themes/sketchbook/theme.css +11 -10
- package/public/themes/sketchbook-dark/theme.css +11 -10
- package/public/themes/sketchbook-light/theme.css +10 -9
- package/public/themes/terminal/theme.css +11 -10
- package/public/themes/terminal-dark/theme.css +11 -10
- package/public/themes/terminal-light/theme.css +11 -10
- package/scripts/theme-a11y.js +11 -31
- package/scripts/theme-contract.json +1 -0
- package/scss/_spacing-scale.scss +25 -0
- package/scss/_utilities.scss +44 -9
- package/scss/components/_accordion.scss +1 -1
- package/scss/components/_data.scss +2 -2
- package/scss/components/_feedback.scss +1 -1
- package/scss/components/_forms.scss +16 -2
- package/scss/components/_navigation.scss +1 -1
- package/scss/examples/_usage.scss +3 -3
- package/scss/recipes/admin-dashboard-layout.md +277 -0
- package/scss/recipes/app-shell.md +339 -0
- package/scss/recipes/auth-flow.md +345 -0
- package/scss/recipes/confirm-dialog.md +248 -0
- package/scss/recipes/data-table.md +382 -0
- package/scss/recipes/datepicker.md +428 -0
- package/scss/recipes/form-validation-async.md +340 -0
- package/scss/recipes/form-validation-html5.md +306 -0
- package/scss/recipes/form-validation-react-hook-form.md +224 -0
- package/scss/recipes/form-validation-success-states.md +290 -0
- package/scss/recipes/form-validation-zod.md +270 -0
- package/scss/recipes/i18n-date-formatting.md +172 -0
- package/scss/recipes/i18n-number-currency.md +145 -0
- package/scss/recipes/i18n-pluralization.md +163 -0
- package/scss/recipes/multi-step-wizard.md +365 -0
- package/scss/recipes/otp-input.md +310 -0
- package/scss/recipes/rtl-layout.md +273 -0
- package/scss/themes/boilerplate-dark.scss +2 -10
- package/scss/themes/boilerplate-light.scss +2 -10
- package/scss/themes/boilerplate.scss +2 -10
- package/scss/themes/cupertino-dark.scss +2 -10
- package/scss/themes/cupertino-light.scss +2 -10
- package/scss/themes/cupertino.scss +2 -10
- package/scss/themes/glass-dark.scss +2 -10
- package/scss/themes/glass-light.scss +2 -10
- package/scss/themes/glass.scss +2 -10
- package/scss/themes/graphite-dark.scss +2 -10
- package/scss/themes/graphite-light.scss +2 -10
- package/scss/themes/graphite.scss +2 -10
- package/scss/themes/press-dark.scss +2 -10
- package/scss/themes/press-light.scss +2 -10
- package/scss/themes/press.scss +2 -10
- package/scss/themes/prism-dark.scss +2 -10
- package/scss/themes/prism-light.scss +2 -10
- package/scss/themes/prism.scss +2 -10
- package/scss/themes/sketchbook-dark.scss +2 -10
- package/scss/themes/sketchbook-light.scss +2 -10
- package/scss/themes/sketchbook.scss +2 -10
- package/scss/themes/terminal-dark.scss +2 -10
- package/scss/themes/terminal-light.scss +2 -10
- package/scss/themes/terminal.scss +2 -10
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cia migrate mui — parse a MUI (Material UI, v5+) theme object + map to cia.
|
|
3
|
+
*
|
|
4
|
+
* v1.2 EPIC-05. Same shape as migrate-tailwind / migrate-bootstrap:
|
|
5
|
+
* 1. Load the consumer's theme module (createTheme({...}) result, or a
|
|
6
|
+
* plain theme-shaped object)
|
|
7
|
+
* 2. Map to cia contract tokens with confidence levels, reusing the shared
|
|
8
|
+
* mapping engine from migrate-tailwind.cjs
|
|
9
|
+
* 3. Write a cia theme.scss (via writeThemeScss)
|
|
10
|
+
*
|
|
11
|
+
* Token-only: this reads MUI's theme VALUES (colors, spacing, radius, font
|
|
12
|
+
* family). It never imports @mui/material and never translates MUI React
|
|
13
|
+
* components — cia has no component library to translate them to. Out of
|
|
14
|
+
* scope, same reason Tailwind/Bootstrap migration doesn't do it either.
|
|
15
|
+
*/
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
// Shared engine from the Tailwind module — no need to fork it.
|
|
22
|
+
const {
|
|
23
|
+
loadConfig,
|
|
24
|
+
writeThemeScss,
|
|
25
|
+
parseFlags,
|
|
26
|
+
closestByRem,
|
|
27
|
+
CIA_SPACING,
|
|
28
|
+
CIA_RADII,
|
|
29
|
+
} = require('./migrate-tailwind.cjs');
|
|
30
|
+
|
|
31
|
+
// ─── theme object discovery ───────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
// createTheme({...}) already returns a fully-resolved plain object — no MUI
|
|
34
|
+
// import needed to read one. Accept a few common export shapes.
|
|
35
|
+
function findMuiTheme(mod) {
|
|
36
|
+
if (mod && typeof mod === 'object') {
|
|
37
|
+
if (mod.theme && mod.theme.palette) return mod.theme;
|
|
38
|
+
if (mod.default && mod.default.palette) return mod.default;
|
|
39
|
+
if (mod.palette) return mod;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ─── color mapping ─────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
const PALETTE_GROUPS = [
|
|
47
|
+
['primary', 'action-primary-default'],
|
|
48
|
+
['secondary', 'action-secondary-default'],
|
|
49
|
+
['error', 'error-default'],
|
|
50
|
+
['warning', 'warning-default'],
|
|
51
|
+
['info', 'info-default'],
|
|
52
|
+
['success', 'success-default'],
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// Deliberately maps ONLY `.main` for each group — writeThemeScss already
|
|
56
|
+
// auto-derives hover/active via m.states() once a `<group>-default` token
|
|
57
|
+
// is set (same as Tailwind/Bootstrap get). Also mapping MUI's own .dark/
|
|
58
|
+
// .light shades would create two competing hover mechanisms in one theme.
|
|
59
|
+
function mapMuiColors(palette) {
|
|
60
|
+
const mappings = {};
|
|
61
|
+
const unmapped = [];
|
|
62
|
+
if (!palette || typeof palette !== 'object') return { mappings, unmapped };
|
|
63
|
+
|
|
64
|
+
for (const [muiKey, ciaToken] of PALETTE_GROUPS) {
|
|
65
|
+
const group = palette[muiKey];
|
|
66
|
+
if (group && typeof group.main === 'string') {
|
|
67
|
+
mappings[ciaToken] = {
|
|
68
|
+
value: group.main,
|
|
69
|
+
source: `palette.${muiKey}.main`,
|
|
70
|
+
confidence: 'HIGH',
|
|
71
|
+
rationale: `MUI 'palette.${muiKey}.main' → cia '${ciaToken}' (direct semantic match; hover/active derive automatically via m.states()).`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (palette.text && typeof palette.text.primary === 'string') {
|
|
77
|
+
mappings['text-primary'] = {
|
|
78
|
+
value: palette.text.primary,
|
|
79
|
+
source: 'palette.text.primary',
|
|
80
|
+
confidence: 'HIGH',
|
|
81
|
+
rationale: `MUI 'palette.text.primary' → cia 'text-primary'.`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (palette.text && typeof palette.text.secondary === 'string') {
|
|
85
|
+
mappings['text-secondary'] = {
|
|
86
|
+
value: palette.text.secondary,
|
|
87
|
+
source: 'palette.text.secondary',
|
|
88
|
+
confidence: 'HIGH',
|
|
89
|
+
rationale: `MUI 'palette.text.secondary' → cia 'text-secondary'.`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (palette.background && typeof palette.background.default === 'string') {
|
|
93
|
+
mappings['background-default'] = {
|
|
94
|
+
value: palette.background.default,
|
|
95
|
+
source: 'palette.background.default',
|
|
96
|
+
confidence: 'HIGH',
|
|
97
|
+
rationale: `MUI 'palette.background.default' → cia 'background-default'.`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (palette.background && typeof palette.background.paper === 'string') {
|
|
101
|
+
mappings['surface-default'] = {
|
|
102
|
+
value: palette.background.paper,
|
|
103
|
+
source: 'palette.background.paper',
|
|
104
|
+
confidence: 'MEDIUM',
|
|
105
|
+
rationale: `MUI 'palette.background.paper' (its elevated-surface concept) → cia 'surface-default'.`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { mappings, unmapped };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ─── spacing mapping ─────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
// MUI's `spacing` is a number (px base unit, default 8), a function
|
|
115
|
+
// `(factor) => value`, or an array of explicit values. Sample factors 1-9
|
|
116
|
+
// (matching cia's own 9-step scale) through whichever form it is.
|
|
117
|
+
function sampleMuiSpacing(spacing) {
|
|
118
|
+
const samples = {};
|
|
119
|
+
const factors = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
120
|
+
|
|
121
|
+
if (typeof spacing === 'number') {
|
|
122
|
+
for (const f of factors) samples[f] = (spacing * f) / 16; // px -> rem
|
|
123
|
+
return samples;
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(spacing)) {
|
|
126
|
+
for (const f of factors) {
|
|
127
|
+
const v = spacing[f];
|
|
128
|
+
if (v == null) continue;
|
|
129
|
+
const n = typeof v === 'number' ? v / 16 : parseFloat(v);
|
|
130
|
+
if (!Number.isNaN(n)) samples[f] = n;
|
|
131
|
+
}
|
|
132
|
+
return samples;
|
|
133
|
+
}
|
|
134
|
+
if (typeof spacing === 'function') {
|
|
135
|
+
for (const f of factors) {
|
|
136
|
+
try {
|
|
137
|
+
const v = spacing(f);
|
|
138
|
+
const n = typeof v === 'number' ? v / 16 : parseFloat(v);
|
|
139
|
+
if (!Number.isNaN(n)) samples[f] = n;
|
|
140
|
+
} catch {
|
|
141
|
+
// consumer's spacing() referenced something outside this sandbox — skip
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return samples;
|
|
145
|
+
}
|
|
146
|
+
return samples;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function mapMuiSpacing(spacing) {
|
|
150
|
+
const mappings = {};
|
|
151
|
+
const unmapped = [];
|
|
152
|
+
const samples = sampleMuiSpacing(spacing);
|
|
153
|
+
const claimedSlots = {};
|
|
154
|
+
|
|
155
|
+
const sorted = Object.entries(samples)
|
|
156
|
+
.map(([factor, rem]) => ({ factor, rem, match: closestByRem(rem, CIA_SPACING) }))
|
|
157
|
+
.sort((a, b) => (a.match ? a.match.deltaRem : Infinity) - (b.match ? b.match.deltaRem : Infinity));
|
|
158
|
+
|
|
159
|
+
for (const { factor, rem, match } of sorted) {
|
|
160
|
+
if (!match) {
|
|
161
|
+
unmapped.push({ source: `spacing(${factor})`, value: `${rem}rem`, reason: `No cia spacing slot within ±0.25rem of ${rem}rem.` });
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const slot = `space-${match.key}`;
|
|
165
|
+
if (claimedSlots[slot]) continue; // closer factor already claimed this slot
|
|
166
|
+
claimedSlots[slot] = factor;
|
|
167
|
+
mappings[slot] = {
|
|
168
|
+
value: `${match.value}rem`,
|
|
169
|
+
source: `spacing(${factor})`,
|
|
170
|
+
confidence: match.confidence,
|
|
171
|
+
rationale: match.confidence === 'HIGH'
|
|
172
|
+
? `MUI 'spacing(${factor})' (${rem}rem) is an exact match for cia ${slot}.`
|
|
173
|
+
: `Closest cia spacing slot to MUI 'spacing(${factor})' (${rem}rem) is ${slot} (${match.value}rem); delta ${match.deltaRem.toFixed(4)}rem.`,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return { mappings, unmapped };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ─── master mapping ─────────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
function mapMuiToCia(theme) {
|
|
182
|
+
const allMappings = {};
|
|
183
|
+
const allUnmapped = [];
|
|
184
|
+
|
|
185
|
+
const colors = mapMuiColors(theme.palette);
|
|
186
|
+
Object.assign(allMappings, colors.mappings);
|
|
187
|
+
allUnmapped.push(...colors.unmapped);
|
|
188
|
+
|
|
189
|
+
if (theme.spacing != null) {
|
|
190
|
+
const spacing = mapMuiSpacing(theme.spacing);
|
|
191
|
+
Object.assign(allMappings, spacing.mappings);
|
|
192
|
+
allUnmapped.push(...spacing.unmapped);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (theme.shape && typeof theme.shape.borderRadius === 'number') {
|
|
196
|
+
const rem = theme.shape.borderRadius / 16;
|
|
197
|
+
const match = closestByRem(rem, CIA_RADII);
|
|
198
|
+
if (match && match.key !== 'full' && match.key !== 'none') {
|
|
199
|
+
allMappings[`radius-${match.key}`] = {
|
|
200
|
+
value: `${match.value}rem`,
|
|
201
|
+
source: 'shape.borderRadius',
|
|
202
|
+
confidence: match.confidence,
|
|
203
|
+
rationale: `MUI 'shape.borderRadius' (${theme.shape.borderRadius}px) is closest to cia radius-${match.key} (${match.value}rem).`,
|
|
204
|
+
};
|
|
205
|
+
} else {
|
|
206
|
+
allUnmapped.push({ source: 'shape.borderRadius', value: `${theme.shape.borderRadius}px`, reason: 'No cia radius slot within ±0.25rem.' });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (theme.typography && typeof theme.typography.fontFamily === 'string') {
|
|
211
|
+
allMappings['font-sans'] = {
|
|
212
|
+
value: theme.typography.fontFamily,
|
|
213
|
+
source: 'typography.fontFamily',
|
|
214
|
+
confidence: 'HIGH',
|
|
215
|
+
rationale: `MUI 'typography.fontFamily' → cia 'font-sans' (direct stack copy).`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Deliberately not mapped — see the epic's scope note: MUI's 24-elevation
|
|
220
|
+
// shadow system has no natural cia analog (cia has 5 named shadow steps),
|
|
221
|
+
// and per-heading typography sizes / breakpoints are a long tail with no
|
|
222
|
+
// single-value confidence story. Surfaced once, informationally, rather
|
|
223
|
+
// than forcing an arbitrary per-field mapping.
|
|
224
|
+
if (Array.isArray(theme.shadows) && theme.shadows.some((s) => s && s !== 'none')) {
|
|
225
|
+
allUnmapped.push({
|
|
226
|
+
source: 'shadows[0..24]',
|
|
227
|
+
value: `${theme.shadows.length} elevation levels`,
|
|
228
|
+
reason: `MUI's 24-elevation shadow system has no 1:1 cia analog (cia has 5 named steps: shadow-sm/md/lg/xl/2xl). Review and pick manually — mapping automatically would be arbitrary, not more correct.`,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (theme.breakpoints && theme.breakpoints.values) {
|
|
232
|
+
allUnmapped.push({
|
|
233
|
+
source: 'breakpoints.values',
|
|
234
|
+
value: JSON.stringify(theme.breakpoints.values),
|
|
235
|
+
reason: `cia's breakpoints are a fixed system scale (see scss/_system.scss), not themed per-project — not migrated. Note for reference only.`,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const counts = { HIGH: 0, MEDIUM: 0, LOW: 0, UNMAPPED: 0 };
|
|
240
|
+
for (const m of Object.values(allMappings)) {
|
|
241
|
+
if (counts[m.confidence] != null) counts[m.confidence] += 1;
|
|
242
|
+
}
|
|
243
|
+
counts.UNMAPPED = allUnmapped.length;
|
|
244
|
+
counts.total = counts.HIGH + counts.MEDIUM + counts.LOW + counts.UNMAPPED;
|
|
245
|
+
|
|
246
|
+
return { mappings: allMappings, unmapped: allUnmapped, report: counts };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ─── CLI entry ────────────────────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
const HELP = `cia migrate mui — convert a MUI (Material UI v5+) theme object to a cia theme.scss
|
|
252
|
+
|
|
253
|
+
Usage:
|
|
254
|
+
cia migrate mui <path> [options]
|
|
255
|
+
|
|
256
|
+
Default behavior:
|
|
257
|
+
Loads a module exporting a MUI theme (the result of createTheme({...}),
|
|
258
|
+
as a default export, a named 'theme' export, or a plain theme-shaped
|
|
259
|
+
object), maps its palette/spacing/shape/typography to cia contract
|
|
260
|
+
tokens with confidence scoring, and writes a cia theme.scss to
|
|
261
|
+
./cia-themes/<name>.scss. Diagnostics print to stderr.
|
|
262
|
+
|
|
263
|
+
Token-only: never imports @mui/material, never translates MUI React
|
|
264
|
+
components — only reads plain values off the theme object.
|
|
265
|
+
|
|
266
|
+
Arguments:
|
|
267
|
+
path Path to a .js/.ts/.mjs/.cjs module exporting a MUI theme.
|
|
268
|
+
|
|
269
|
+
Options:
|
|
270
|
+
--name <name> Theme name. Default: migrated
|
|
271
|
+
--out <path> Custom output path. Default: ./cia-themes/<name>.scss
|
|
272
|
+
--json Skip the file write and dump JSON to stdout (pipe-safe).
|
|
273
|
+
-h, --help Show this help.
|
|
274
|
+
|
|
275
|
+
Examples:
|
|
276
|
+
cia migrate mui ./src/theme.ts
|
|
277
|
+
cia migrate mui ./src/theme.ts --name acme
|
|
278
|
+
cia migrate mui ./src/theme.ts --json | jq '.cia.report'
|
|
279
|
+
|
|
280
|
+
Notes:
|
|
281
|
+
- MUI's 24-elevation shadow system and per-breakpoint values are not
|
|
282
|
+
mapped automatically — see the UNMAPPED block for why.
|
|
283
|
+
- Hover/active states are NOT read from palette.<group>.dark/.light —
|
|
284
|
+
cia derives them automatically from the -default value via m.states().
|
|
285
|
+
`;
|
|
286
|
+
|
|
287
|
+
async function run(args) {
|
|
288
|
+
const { flags, positional } = parseFlags(args);
|
|
289
|
+
if (flags.help) {
|
|
290
|
+
process.stdout.write(HELP);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
let themePath = positional[0];
|
|
295
|
+
if (!themePath) {
|
|
296
|
+
throw new Error(`cia migrate mui requires a path to a module exporting a MUI theme. Example: cia migrate mui ./src/theme.ts`);
|
|
297
|
+
}
|
|
298
|
+
if (!path.isAbsolute(themePath)) themePath = path.resolve(process.cwd(), themePath);
|
|
299
|
+
if (!fs.existsSync(themePath)) throw new Error(`theme file not found: ${themePath}`);
|
|
300
|
+
|
|
301
|
+
process.stderr.write(`cia migrate mui\n`);
|
|
302
|
+
process.stderr.write(` theme: ${themePath}\n`);
|
|
303
|
+
process.stderr.write(` loading...\n`);
|
|
304
|
+
|
|
305
|
+
const mod = loadConfig(themePath);
|
|
306
|
+
const theme = findMuiTheme(mod);
|
|
307
|
+
if (!theme) {
|
|
308
|
+
throw new Error(
|
|
309
|
+
`Could not find a MUI theme in ${themePath}. Expected a default export, a named ` +
|
|
310
|
+
`'theme' export, or the module itself to be a theme-shaped object with a 'palette' key ` +
|
|
311
|
+
`(the result of createTheme({...})).`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
process.stderr.write(` found theme (mode: ${theme.palette?.mode ?? 'light'})\n`);
|
|
316
|
+
process.stderr.write(`\n mapping to cia contract tokens...\n`);
|
|
317
|
+
const cia = mapMuiToCia(theme);
|
|
318
|
+
|
|
319
|
+
process.stderr.write(`\n ─── confidence report ─────────────────────────\n`);
|
|
320
|
+
process.stderr.write(` HIGH (exact match): ${cia.report.HIGH}\n`);
|
|
321
|
+
process.stderr.write(` MEDIUM (close, ≤0.125rem): ${cia.report.MEDIUM}\n`);
|
|
322
|
+
process.stderr.write(` LOW (best guess): ${cia.report.LOW}\n`);
|
|
323
|
+
process.stderr.write(` UNMAPPED (no cia analog): ${cia.report.UNMAPPED}\n`);
|
|
324
|
+
process.stderr.write(` ─────────────────────────────────────────────\n`);
|
|
325
|
+
process.stderr.write(` total ${cia.report.total}\n`);
|
|
326
|
+
|
|
327
|
+
if (flags.json) {
|
|
328
|
+
process.stderr.write(`\n --- JSON dump on stdout below ---\n`);
|
|
329
|
+
process.stdout.write(JSON.stringify({ source: themePath, cia }, null, 2));
|
|
330
|
+
process.stdout.write('\n');
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const scss = writeThemeScss(cia, { name: flags.name, source: themePath, tool: 'mui' });
|
|
335
|
+
let outPath = flags.out;
|
|
336
|
+
if (outPath) {
|
|
337
|
+
if (!path.isAbsolute(outPath)) outPath = path.resolve(process.cwd(), outPath);
|
|
338
|
+
} else {
|
|
339
|
+
outPath = path.join(process.cwd(), 'cia-themes', `${flags.name}.scss`);
|
|
340
|
+
}
|
|
341
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
342
|
+
fs.writeFileSync(outPath, scss, 'utf8');
|
|
343
|
+
|
|
344
|
+
process.stderr.write(`\n wrote ${outPath}\n`);
|
|
345
|
+
process.stderr.write(` ${scss.split('\n').length - 1} lines, ${Object.keys(cia.mappings).length} mapped tokens\n`);
|
|
346
|
+
if (cia.report.UNMAPPED > 0) {
|
|
347
|
+
process.stderr.write(` ${cia.report.UNMAPPED} unmapped/informational entries appended as a comment block — review\n`);
|
|
348
|
+
}
|
|
349
|
+
process.stderr.write(`\n Next steps:\n`);
|
|
350
|
+
process.stderr.write(` 1. Review the file (MEDIUM/LOW/UNMAPPED entries are flagged)\n`);
|
|
351
|
+
process.stderr.write(` 2. @use this file from your app's SCSS entry\n`);
|
|
352
|
+
process.stderr.write(` 3. Set <html data-theme="${flags.name}"> and run npm run build:css\n`);
|
|
353
|
+
process.stderr.write(` 4. Run npm run validate-themes to confirm WCAG 2.2 AA contrast\n`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
module.exports = {
|
|
357
|
+
run,
|
|
358
|
+
findMuiTheme,
|
|
359
|
+
mapMuiColors,
|
|
360
|
+
mapMuiSpacing,
|
|
361
|
+
mapMuiToCia,
|
|
362
|
+
sampleMuiSpacing,
|
|
363
|
+
};
|
package/bin/migrate-tailwind.cjs
CHANGED
|
@@ -586,7 +586,8 @@ function writeThemeScss(cia, options = {}) {
|
|
|
586
586
|
timestamp = new Date().toISOString().slice(0, 19) + 'Z',
|
|
587
587
|
} = options;
|
|
588
588
|
|
|
589
|
-
const
|
|
589
|
+
const TOOL_LABELS = { bootstrap: 'Bootstrap values', mui: 'MUI values', chakra: 'Chakra values', tailwind: 'Tailwind values' };
|
|
590
|
+
const sourceLabel = TOOL_LABELS[tool] || 'Tailwind values';
|
|
590
591
|
const lines = [];
|
|
591
592
|
|
|
592
593
|
// ── Header (block comment so consumer sees it before any SCSS) ──
|
|
@@ -812,6 +813,7 @@ async function run(args) {
|
|
|
812
813
|
module.exports = {
|
|
813
814
|
run,
|
|
814
815
|
findTailwindConfig,
|
|
816
|
+
loadConfig,
|
|
815
817
|
extractTheme,
|
|
816
818
|
summary,
|
|
817
819
|
// Mapping internals exported for unit testing + external introspection
|