emily-css 1.1.1 → 1.2.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 CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to `emily-css` are documented here.
4
4
 
5
+ ---
6
+ ## v1.2.0-alpha.0 — May 2026
7
+
8
+ ### Added
9
+ - Report-only Tailwind-to-EmilyCSS migration command: `emily-css migrate`.
10
+ - Default semantic migration mode for design-token aligned suggestions.
11
+ - Imported palette mode via `emily-css migrate --import-colours` for visual parity mapping suggestions.
12
+ - Detection and reporting for arbitrary value utilities during migration analysis.
13
+
14
+ ### Notes
15
+ - Migration in this alpha is analysis-only: no source files are modified by `migrate`.
16
+
5
17
  ---
6
18
  ## v1.1.0 — May 2026
7
19
 
@@ -11,6 +23,17 @@ All notable changes to `emily-css` are documented here.
11
23
 
12
24
  ---
13
25
 
26
+ ## v1.2.1 — May 2026
27
+
28
+ **updated the full system to be more efficient**
29
+
30
+ ### Added
31
+ - updated the full system to be more efficient
32
+
33
+ ### Changed
34
+ - updated release logic
35
+
36
+ ---
14
37
  ## v1.1.1 — May 2026
15
38
 
16
39
  **updated changes and added**
package/README.md CHANGED
@@ -53,8 +53,17 @@ npx emily-css init # Setup config + first build
53
53
  npx emily-css build # Regenerate CSS
54
54
  npx emily-css watch # Development watch mode
55
55
  npx emily-css purge # Remove unused styles for production
56
+ npx emily-css migrate # Report-only Tailwind-to-EmilyCSS migration analysis
57
+ --import-colours # Imported palette mode (visual parity class suggestions)
56
58
  ```
57
59
 
60
+ ## Migration (1.2.0-alpha)
61
+
62
+ - `emily-css migrate` is report-only and does not modify files.
63
+ - Default migration mode is semantic (`gray/slate/zinc/stone` remap toward `neutral` naming).
64
+ - `emily-css migrate --import-colours` enables imported palette mode for parity-oriented palette suggestions.
65
+ - Arbitrary value utilities (for example `w-[37px]`, `bg-[#0f172a]`) are detected and reported as unsupported.
66
+
58
67
  ## How Purge Works
59
68
 
60
69
  emilyCSS scans your templates for used class names and removes everything else.
@@ -146,4 +155,4 @@ Then import the weights you need.
146
155
 
147
156
  ## License
148
157
 
149
- MIT
158
+ MIT
package/bin/emilyui.js CHANGED
@@ -4,6 +4,21 @@ const path = require("path");
4
4
 
5
5
  const command = process.argv[2];
6
6
  const packageJson = require(path.join(__dirname, "..", "package.json"));
7
+ const usageText = `
8
+ emily-css — Config-driven CSS framework generator
9
+
10
+ Usage:
11
+ emily-css init Set up a new project
12
+ emily-css build Generate production CSS to the configured output path
13
+ emily-css watch Dev mode: rebuild on changes
14
+ emily-css doctor Scan project files for unknown EmilyCSS classes
15
+ emily-css migrate Generate a Tailwind-to-EmilyCSS migration report
16
+ --import-colours Detect Tailwind colour palettes and suggest importedPalettes config
17
+ emily-css showcase Browse components in your browser
18
+ emily-css help Full command reference
19
+
20
+ Run emily-css help for more detail.
21
+ `;
7
22
 
8
23
  if (command === "init") {
9
24
  require("../src/init.js");
@@ -18,6 +33,12 @@ if (command === "init") {
18
33
  const { doctor } = require("../src/doctor.js");
19
34
  const result = doctor();
20
35
  process.exitCode = result.exitCode;
36
+ } else if (command === "migrate") {
37
+ const { generateMigrationReport } = require("../src/migrate.js");
38
+ const { formatMigrationReport } = require("../src/reporters/migrationReporter.js");
39
+ const importColours = process.argv.includes("--import-colours");
40
+ const report = generateMigrationReport({ importColours });
41
+ console.log(formatMigrationReport(report, { importColours }));
21
42
  } else if (command === "version" || command === "--version" || command === "-v") {
22
43
  console.log(packageJson.version);
23
44
  } else if (command === "help") {
@@ -29,6 +50,8 @@ if (command === "init") {
29
50
  emily-css build Generate production CSS to the configured output path
30
51
  emily-css watch Dev mode: watch for changes and rebuild
31
52
  emily-css doctor Scan project files for unknown EmilyCSS classes
53
+ emily-css migrate Generate a Tailwind-to-EmilyCSS migration report
54
+ --import-colours Detect Tailwind colour palettes and suggest importedPalettes config
32
55
  emily-css showcase Launch the component showcase in your browser
33
56
  emily-css version Show installed version
34
57
  emily-css help Show this help text
@@ -42,17 +65,11 @@ if (command === "init") {
42
65
  Docs: https://emilyui.dev
43
66
  `);
44
67
  } else {
45
- console.log(`
46
- emily-css — Config-driven CSS framework generator
47
-
48
- Usage:
49
- emily-css init Set up a new project
50
- emily-css build Generate production CSS to the configured output path
51
- emily-css watch Dev mode: rebuild on changes
52
- emily-css doctor Scan project files for unknown EmilyCSS classes
53
- emily-css showcase Browse components in your browser
54
- emily-css help Full command reference
55
-
56
- Run emily-css help for more detail.
57
- `);
68
+ if (!command) {
69
+ console.log(usageText);
70
+ } else {
71
+ console.error(`Unknown command: ${command}`);
72
+ console.log(usageText);
73
+ process.exitCode = 1;
74
+ }
58
75
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emily-css",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "A config-driven utility CSS framework. Define your brand once, generate the CSS.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -0,0 +1,65 @@
1
+ const DEFAULT_EXTENSIONS = [
2
+ '.html',
3
+ '.htm',
4
+ '.twig',
5
+ '.njk',
6
+ '.liquid',
7
+ '.hbs',
8
+ '.js',
9
+ '.jsx',
10
+ '.ts',
11
+ '.tsx',
12
+ '.vue',
13
+ '.php',
14
+ '.astro',
15
+ '.svelte',
16
+ '.blade.php',
17
+ '.jinja',
18
+ '.jinja2',
19
+ '.j2',
20
+ '.md',
21
+ ];
22
+
23
+ const DEFAULT_PURGE_IGNORE = [
24
+ 'node_modules',
25
+ '.git',
26
+ '.nuxt',
27
+ '.next',
28
+ '.output',
29
+ 'dist',
30
+ 'build',
31
+ 'coverage',
32
+ '.cache',
33
+ '.vite',
34
+ ];
35
+
36
+ const DEFAULT_RESPONSIVE_VARIANTS = ['sm', 'md', 'lg', 'xl', '2xl'];
37
+
38
+ const BASE_VARIANTS = [
39
+ 'hover',
40
+ 'focus',
41
+ 'focus-within',
42
+ 'focus-visible',
43
+ 'active',
44
+ 'disabled',
45
+ 'motion-reduce',
46
+ 'motion-safe',
47
+ 'aria-expanded',
48
+ 'aria-selected',
49
+ 'aria-current',
50
+ 'aria-disabled',
51
+ 'data-open',
52
+ 'data-closed',
53
+ 'dark',
54
+ 'forced-colors',
55
+ ];
56
+
57
+ const PURGE_EXTENSIONS = [...DEFAULT_EXTENSIONS];
58
+
59
+ module.exports = {
60
+ DEFAULT_EXTENSIONS,
61
+ DEFAULT_PURGE_IGNORE,
62
+ DEFAULT_RESPONSIVE_VARIANTS,
63
+ BASE_VARIANTS,
64
+ PURGE_EXTENSIONS,
65
+ };
package/src/doctor.js CHANGED
@@ -3,28 +3,7 @@ const path = require("path");
3
3
  const fg = require("fast-glob");
4
4
  const { extractClassNames } = require("./purge.js");
5
5
  const { ensureFullFramework, generateManifest } = require("./index.js");
6
-
7
- const DEFAULT_EXTENSIONS = [
8
- ".html",
9
- ".htm",
10
- ".twig",
11
- ".njk",
12
- ".liquid",
13
- ".hbs",
14
- ".js",
15
- ".jsx",
16
- ".ts",
17
- ".tsx",
18
- ".vue",
19
- ".php",
20
- ".astro",
21
- ".svelte",
22
- ".blade.php",
23
- ".jinja",
24
- ".jinja2",
25
- ".j2",
26
- ".md",
27
- ];
6
+ const { DEFAULT_EXTENSIONS } = require("./constants.js");
28
7
 
29
8
  function getConfigPath() {
30
9
  return path.join(process.cwd(), "emily.config.json");
package/src/generators.js CHANGED
@@ -281,6 +281,7 @@ function transitionUtilities() {
281
281
  // Transforms
282
282
  function transformUtilities(spacing) {
283
283
  let css = `/* Transforms */\n`;
284
+ const composedTransform = 'translate(var(--translate-x, 0), var(--translate-y, 0)) rotate(var(--rotate, 0)) skewX(var(--skew-x, 0)) skewY(var(--skew-y, 0)) scaleX(var(--scale-x, 1)) scaleY(var(--scale-y, 1))';
284
285
 
285
286
  css += `.transform { transform: translateZ(0); }\n`;
286
287
  css += `.transform-gpu { transform: translate3d(0, 0, 0); }\n`;
@@ -289,30 +290,30 @@ function transformUtilities(spacing) {
289
290
  // Translate
290
291
  Object.entries(spacing).forEach(([key, value]) => {
291
292
  const escaped = escapeClassName(key);
292
- css += `.translate-x-${escaped} { transform: translateX(${value}); }\n`;
293
- css += `.translate-y-${escaped} { transform: translateY(${value}); }\n`;
294
- css += `.-translate-x-${escaped} { transform: translateX(-${value}); }\n`;
295
- css += `.-translate-y-${escaped} { transform: translateY(-${value}); }\n`;
293
+ css += `.translate-x-${escaped} { --translate-x: ${value}; transform: ${composedTransform}; }\n`;
294
+ css += `.translate-y-${escaped} { --translate-y: ${value}; transform: ${composedTransform}; }\n`;
295
+ css += `.-translate-x-${escaped} { --translate-x: -${value}; transform: ${composedTransform}; }\n`;
296
+ css += `.-translate-y-${escaped} { --translate-y: -${value}; transform: ${composedTransform}; }\n`;
296
297
  });
297
298
 
298
299
  // Rotate
299
300
  const rotations = [0, 1, 2, 3, 6, 12, 45, 90, 180];
300
301
  rotations.forEach(deg => {
301
- css += `.rotate-${deg} { transform: rotate(${deg}deg); }\n`;
302
- if (deg > 0) css += `.-rotate-${deg} { transform: rotate(-${deg}deg); }\n`;
302
+ css += `.rotate-${deg} { --rotate: ${deg}deg; transform: ${composedTransform}; }\n`;
303
+ if (deg > 0) css += `.-rotate-${deg} { --rotate: -${deg}deg; transform: ${composedTransform}; }\n`;
303
304
  });
304
305
 
305
306
  // Scale
306
307
  const scales = [0, 50, 75, 90, 95, 100, 110, 125, 150];
307
308
  scales.forEach(scale => {
308
- css += `.scale-${scale} { transform: scale(${scale / 100}); }\n`;
309
+ css += `.scale-${scale} { --scale-x: ${scale / 100}; --scale-y: ${scale / 100}; transform: ${composedTransform}; }\n`;
309
310
  });
310
311
 
311
312
  // Skew
312
313
  const skews = [0, 1, 2, 3];
313
314
  skews.forEach(sk => {
314
- css += `.skew-x-${sk} { transform: skewX(${sk}deg); }\n`;
315
- css += `.skew-y-${sk} { transform: skewY(${sk}deg); }\n`;
315
+ css += `.skew-x-${sk} { --skew-x: ${sk}deg; transform: ${composedTransform}; }\n`;
316
+ css += `.skew-y-${sk} { --skew-y: ${sk}deg; transform: ${composedTransform}; }\n`;
316
317
  });
317
318
 
318
319
  // Transform origin
@@ -349,18 +350,21 @@ function shadowUtilities() {
349
350
  function ringUtilities(colours) {
350
351
  let css = `/* Rings & Outlines */\n`;
351
352
 
352
- css += `.ring-0 { box-shadow: 0 0 0 0px var(--ring-color, transparent); }\n`;
353
- css += `.ring-1 { box-shadow: 0 0 0 1px var(--ring-color, transparent); }\n`;
354
- css += `.ring-2 { box-shadow: 0 0 0 2px var(--ring-color, transparent); }\n`;
353
+ css += `.ring-0 { --ring-offset-width: 0px; --ring-offset-color: #fff; --ring-color: currentColor; box-shadow: 0 0 0 var(--ring-offset-width, 0px) var(--ring-offset-color, #fff), 0 0 0 var(--ring-offset-width, 0px) transparent; }\n`;
354
+ css += `.ring-1 { --ring-offset-width: 0px; --ring-offset-color: #fff; --ring-color: currentColor; box-shadow: 0 0 0 var(--ring-offset-width, 0px) var(--ring-offset-color, #fff), 0 0 0 calc(1px + var(--ring-offset-width, 0px)) var(--ring-color, currentColor); }\n`;
355
+ css += `.ring-2 { --ring-offset-width: 0px; --ring-offset-color: #fff; --ring-color: currentColor; box-shadow: 0 0 0 var(--ring-offset-width, 0px) var(--ring-offset-color, #fff), 0 0 0 calc(2px + var(--ring-offset-width, 0px)) var(--ring-color, currentColor); }\n`;
355
356
 
356
357
  css += `.ring-offset-0 { --ring-offset-width: 0px; }\n`;
357
358
  css += `.ring-offset-2 { --ring-offset-width: 2px; }\n`;
358
359
  css += `.ring-offset-4 { --ring-offset-width: 4px; }\n`;
360
+ css += `.ring-offset-white { --ring-offset-color: #fff; }\n`;
361
+ css += `.ring-offset-black { --ring-offset-color: #000; }\n`;
359
362
 
360
363
  // Ring colours
361
364
  Object.entries(colours).forEach(([colourName, shades]) => {
362
365
  Object.entries(shades).forEach(([shade]) => {
363
366
  css += `.ring-${colourName}-${shade} { --ring-color: var(--color-${colourName}-${shade}); }\n`;
367
+ css += `.ring-offset-${colourName}-${shade} { --ring-offset-color: var(--color-${colourName}-${shade}); }\n`;
364
368
  });
365
369
  });
366
370
 
@@ -642,13 +646,10 @@ function accessibilityUtilities() {
642
646
  // Container Queries (Forward-looking)
643
647
  function containerUtilities() {
644
648
  return `/* Container Queries */
645
- @supports (container-type: inline-size) {
646
- .container-type-inline { container-type: inline-size; }
647
- @container (min-width: 20rem) { .cq-xs\\: { /* utilities */ } }
648
- @container (min-width: 28rem) { .cq-sm\\: { /* utilities */ } }
649
- @container (min-width: 36rem) { .cq-md\\: { /* utilities */ } }
650
- @container (min-width: 48rem) { .cq-lg\\: { /* utilities */ } }
651
- }
649
+ .container-type-inline { container-type: inline-size; }
650
+ .container-type-size { container-type: size; }
651
+ .container-type-normal { container-type: normal; }
652
+ .container-name-none { container-name: none; }
652
653
 
653
654
  `;
654
655
  }
package/src/index.js CHANGED
@@ -235,6 +235,7 @@ function generateCSSVariables(colours, spacing, config) {
235
235
  css += ` --color-${name}: ${hex};\n`;
236
236
  });
237
237
  }
238
+ css += ` --focus-ring-glow: color-mix(in srgb, var(--color-brand-80) 12%, transparent);\n`;
238
239
 
239
240
  // Spacing variables
240
241
  Object.entries(spacing).forEach(([key, value]) => {
@@ -1241,7 +1242,7 @@ function generatePatternComponents() {
1241
1242
  outline: 2px solid var(--color-neutral-80);
1242
1243
  outline-offset: 3px;
1243
1244
  border-color: var(--color-neutral-80);
1244
- box-shadow: 0 0 0 4px rgba(219, 39, 119, 0.1);
1245
+ box-shadow: 0 0 0 4px var(--focus-ring-glow, rgba(219, 39, 119, 0.1));
1245
1246
  }
1246
1247
 
1247
1248
  .checkbox-group,
@@ -1272,7 +1273,7 @@ function generatePatternComponents() {
1272
1273
  input[type="checkbox"]:focus {
1273
1274
  outline: 2px solid var(--color-neutral-80);
1274
1275
  outline-offset: 3px;
1275
- box-shadow: 0 0 0 4px rgba(219, 39, 119, 0.1);
1276
+ box-shadow: 0 0 0 4px var(--focus-ring-glow, rgba(219, 39, 119, 0.1));
1276
1277
  }
1277
1278
 
1278
1279
  input[type="radio"] {
@@ -1317,7 +1318,7 @@ function generatePatternComponents() {
1317
1318
  outline: 2px solid var(--color-neutral-80);
1318
1319
  outline-offset: 3px;
1319
1320
  border-radius: 50%;
1320
- box-shadow: 0 0 0 4px rgba(219, 39, 119, 0.1);
1321
+ box-shadow: 0 0 0 4px var(--focus-ring-glow, rgba(219, 39, 119, 0.1));
1321
1322
  }
1322
1323
 
1323
1324
  input[aria-invalid="true"] {
@@ -1382,7 +1383,7 @@ function generatePatternComponents() {
1382
1383
  .btn-primary:focus-visible {
1383
1384
  outline: 2px solid var(--color-neutral-80);
1384
1385
  outline-offset: 3px;
1385
- box-shadow: 0 0 0 4px rgba(219, 39, 119, 0.1);
1386
+ box-shadow: 0 0 0 4px var(--focus-ring-glow, rgba(219, 39, 119, 0.1));
1386
1387
  }
1387
1388
 
1388
1389
  .btn-secondary {
@@ -1400,7 +1401,7 @@ function generatePatternComponents() {
1400
1401
  .btn-secondary:focus-visible {
1401
1402
  outline: 2px solid var(--color-neutral-80);
1402
1403
  outline-offset: 3px;
1403
- box-shadow: 0 0 0 4px rgba(219, 39, 119, 0.1);
1404
+ box-shadow: 0 0 0 4px var(--focus-ring-glow, rgba(219, 39, 119, 0.1));
1404
1405
  }
1405
1406
 
1406
1407
  .btn-ghost {
@@ -1416,7 +1417,7 @@ function generatePatternComponents() {
1416
1417
  .btn-ghost:focus-visible {
1417
1418
  outline: 2px solid var(--color-neutral-80);
1418
1419
  outline-offset: 3px;
1419
- box-shadow: 0 0 0 4px rgba(219, 39, 119, 0.1);
1420
+ box-shadow: 0 0 0 4px var(--focus-ring-glow, rgba(219, 39, 119, 0.1));
1420
1421
  }
1421
1422
 
1422
1423
  .btn-danger {
@@ -1432,7 +1433,7 @@ function generatePatternComponents() {
1432
1433
  .btn-danger:focus-visible {
1433
1434
  outline: 2px solid var(--color-neutral-80);
1434
1435
  outline-offset: 3px;
1435
- box-shadow: 0 0 0 4px rgba(219, 39, 119, 0.1);
1436
+ box-shadow: 0 0 0 4px var(--focus-ring-glow, rgba(219, 39, 119, 0.1));
1436
1437
  }
1437
1438
 
1438
1439
  .btn-sm {
package/src/init.js CHANGED
@@ -5,24 +5,12 @@ const { Select, Input, Confirm } = require("enquirer");
5
5
  const chalk = require("chalk");
6
6
  const ora = require("ora");
7
7
  const boxen = require("boxen");
8
+ const { DEFAULT_PURGE_IGNORE, PURGE_EXTENSIONS } = require("./constants.js");
8
9
 
9
10
  // ============================================================================
10
11
  // CONSTANTS
11
12
  // ============================================================================
12
13
 
13
- const DEFAULT_PURGE_IGNORE = [
14
- "node_modules",
15
- ".git",
16
- ".nuxt",
17
- ".next",
18
- ".output",
19
- "dist",
20
- "build",
21
- "coverage",
22
- ".cache",
23
- ".vite",
24
- ];
25
-
26
14
  const COLOUR_PRESETS = {
27
15
  primary: [
28
16
  { value: "custom", label: "Enter your own hex" },
@@ -69,28 +57,6 @@ const FONT_OPTIONS = [
69
57
  { name: "system", message: "System sans-serif (no download required)" },
70
58
  ];
71
59
 
72
- const PURGE_EXTENSIONS = [
73
- ".html",
74
- ".htm",
75
- ".twig",
76
- ".njk",
77
- ".liquid",
78
- ".hbs",
79
- ".js",
80
- ".jsx",
81
- ".ts",
82
- ".tsx",
83
- ".vue",
84
- ".php",
85
- ".astro",
86
- ".svelte",
87
- ".blade.php",
88
- ".jinja",
89
- ".jinja2",
90
- ".j2",
91
- ".md",
92
- ];
93
-
94
60
  // ============================================================================
95
61
  // HELPERS
96
62
  // ============================================================================
@@ -662,7 +628,7 @@ async function init() {
662
628
 
663
629
  const baseUnitRaw = await new Input({
664
630
  name: "baseUnit",
665
- message: "Base spacing unit in px (18px = 1.125rem)",
631
+ message: "Base spacing unit in px (label/documentation only)",
666
632
  initial: "18",
667
633
  validate: function (value) {
668
634
  const parsed = Number.parseInt(value, 10);
package/src/manifest.js CHANGED
@@ -1,23 +1,8 @@
1
1
  const MANIFEST_VERSION = '1.1.0';
2
- const DEFAULT_RESPONSIVE_VARIANTS = ['sm', 'md', 'lg', 'xl', '2xl'];
3
- const BASE_VARIANTS = [
4
- 'hover',
5
- 'focus',
6
- 'focus-within',
7
- 'focus-visible',
8
- 'active',
9
- 'disabled',
10
- 'motion-reduce',
11
- 'motion-safe',
12
- 'aria-expanded',
13
- 'aria-selected',
14
- 'aria-current',
15
- 'aria-disabled',
16
- 'data-open',
17
- 'data-closed',
18
- 'dark',
19
- 'forced-colors',
20
- ];
2
+ const {
3
+ DEFAULT_RESPONSIVE_VARIANTS,
4
+ BASE_VARIANTS,
5
+ } = require('./constants.js');
21
6
 
22
7
  function parseDeclarations(block) {
23
8
  const declarations = {};