emily-css 1.0.27 → 1.0.29

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
@@ -4,6 +4,22 @@ All notable changes to `emily-css` are documented here.
4
4
 
5
5
  ---
6
6
 
7
+ ## v1.0.29 — May 2026
8
+
9
+ **added json manifest for future use**
10
+
11
+ ### Added
12
+ - added json manifest for future use
13
+
14
+ ---
15
+ ## v1.0.28 — May 2026
16
+
17
+ **added new utilities**
18
+
19
+ ### Changed
20
+ - added new utilties and tests
21
+
22
+ ---
7
23
  ## v1.0.27 — May 2026
8
24
 
9
25
  **colour system redesign — brand/accent tokens + semantic colours (v1.0.23)**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emily-css",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
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": {
package/src/generators.js CHANGED
@@ -424,6 +424,12 @@ function svgUtilities(colours) {
424
424
 
425
425
  css += `.fill-current { fill: currentColor; }\n`;
426
426
  css += `.stroke-current { stroke: currentColor; }\n`;
427
+ css += `.fill-white { fill: #FAFAFA; }\n`;
428
+ css += `.fill-black { fill: #111110; }\n`;
429
+ css += `.fill-transparent { fill: transparent; }\n`;
430
+ css += `.stroke-white { stroke: #FAFAFA; }\n`;
431
+ css += `.stroke-black { stroke: #111110; }\n`;
432
+ css += `.stroke-transparent { stroke: transparent; }\n`;
427
433
  css += `.stroke-0 { stroke-width: 0; }\n`;
428
434
  css += `.stroke-1 { stroke-width: 1; }\n`;
429
435
  css += `.stroke-2 { stroke-width: 2; }\n`;
@@ -602,6 +608,10 @@ function accessibilityUtilities() {
602
608
  return `/* Accessibility */
603
609
  .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; }
604
610
  .not-sr-only { position: static; width: auto; height: auto; padding: 0; margin: 0; overflow: visible; clip: auto; white-space: normal; }
611
+ .sr-only-focusable:not(:focus):not(:focus-within) { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; }
612
+ .focus-ring:focus-visible { outline: 2px solid var(--color-brand-80); outline-offset: 2px; }
613
+ .focus-ring-inset:focus-visible { outline: 2px solid var(--color-brand-80); outline-offset: -2px; }
614
+ .focus-ring-none:focus-visible { outline: none; }
605
615
  .focus-visible:focus { outline: 2px solid currentColor; outline-offset: 2px; }
606
616
  .focus\\:outline-none:focus { outline: 2px solid transparent; outline-offset: 2px; }
607
617
 
@@ -756,8 +766,8 @@ function divideUtilities(spacing, colours) {
756
766
  css += `.divide-${colourName}-${shade} > * + * { border-color: var(--color-${colourName}-${shade}); }\n`;
757
767
  });
758
768
  });
759
- css += `.divide-white > * + * { border-color: #ffffff; }\n`;
760
- css += `.divide-black > * + * { border-color: #000000; }\n`;
769
+ css += `.divide-white > * + * { border-color: #FAFAFA; }\n`;
770
+ css += `.divide-black > * + * { border-color: #111110; }\n`;
761
771
  css += `.divide-transparent > * + * { border-color: transparent; }\n`;
762
772
  css += `\n`;
763
773
  return css;
package/src/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const { generateManifest } = require('./manifest');
6
7
 
7
8
 
8
9
  // ============================================================================
@@ -693,8 +694,8 @@ function generateBorderUtilities(config) {
693
694
  css += `.border-none { border-style: none; }\n`;
694
695
  css += `.border-transparent { border-color: transparent; }\n`;
695
696
  css += `.border-current { border-color: currentColor; }\n`;
696
- css += `.border-black { border-color: #000000; }\n`;
697
- css += `.border-white { border-color: #ffffff; }\n`;
697
+ css += `.border-black { border-color: #111110; }\n`;
698
+ css += `.border-white { border-color: #FAFAFA; }\n`;
698
699
 
699
700
  const baseRadius = config.spacing.borderRadius['base'] || '8px';
700
701
  css += `.rounded { border-radius: ${baseRadius}; }\n`;
@@ -791,9 +792,15 @@ function generateColourUtilities(colours) {
791
792
  });
792
793
  });
793
794
 
794
- css += `.bg-white { background-color: #ffffff; }\n`;
795
+ css += `.bg-white { background-color: #FAFAFA; }\n`;
796
+ css += `.bg-black { background-color: #111110; }\n`;
795
797
  css += `.bg-transparent { background-color: transparent; }\n`;
796
- css += `.text-white { color: #ffffff; }\n`;
798
+ css += `.bg-current { background-color: currentColor; }\n`;
799
+
800
+ css += `.text-white { color: #FAFAFA; }\n`;
801
+ css += `.text-black { color: #111110; }\n`;
802
+ css += `.text-transparent { color: transparent; }\n`;
803
+ css += `.text-current { color: currentColor; }\n`;
797
804
 
798
805
  css += `\n`;
799
806
  return css;
@@ -812,6 +819,63 @@ function generateSemanticColourUtilities(semanticColours) {
812
819
  return css;
813
820
  }
814
821
 
822
+ // ============================================================================
823
+ // ARIA & DATA-STATE VARIANTS
824
+ // ============================================================================
825
+ // Generates ARIA attribute and data-state variants for all utility classes.
826
+ // Selectors target the attribute value directly so they work without JS —
827
+ // just toggle the attribute and the utility activates.
828
+ //
829
+ // Usage in HTML:
830
+ // aria-expanded: class="aria-expanded:block" aria-expanded="true"
831
+ // data-open: class="data-open:flex" data-state="open"
832
+ //
833
+ // Output examples:
834
+ // .aria-expanded\:block[aria-expanded="true"] { display: block; }
835
+ // .data-open\:flex[data-state="open"] { display: flex; }
836
+
837
+ function addAriaDataVariants(css) {
838
+ const variants = [
839
+ { name: 'aria-expanded', selector: '[aria-expanded="true"]' },
840
+ { name: 'aria-selected', selector: '[aria-selected="true"]' },
841
+ { name: 'aria-current', selector: '[aria-current="page"]' },
842
+ { name: 'aria-disabled', selector: '[aria-disabled="true"]' },
843
+ { name: 'data-open', selector: '[data-state="open"]' },
844
+ { name: 'data-closed', selector: '[data-state="closed"]' },
845
+ ];
846
+
847
+ let variantCss = css;
848
+
849
+ variants.forEach(variant => {
850
+ let variantRules = '';
851
+ const lines = css.split('\n');
852
+
853
+ lines.forEach(line => {
854
+ if (line.startsWith('.') && line.includes('{')) {
855
+ const className = line.split('{')[0].trim();
856
+ // Skip already-variant lines (contain ':' in class name) and special selectors
857
+ if (
858
+ !className.startsWith(':root') &&
859
+ !className.includes('@') &&
860
+ !className.includes('::') &&
861
+ !className.includes(':')
862
+ ) {
863
+ const classWithoutDot = className.substring(1);
864
+ const ariaSelector = `.${variant.name}\\:${classWithoutDot}${variant.selector}`;
865
+ const ariaRule = line.replace(className, ariaSelector);
866
+ variantRules += ariaRule + '\n';
867
+ }
868
+ }
869
+ });
870
+
871
+ if (variantRules) {
872
+ variantCss += `\n/* ARIA/data-state variant: ${variant.name} */\n` + variantRules;
873
+ }
874
+ });
875
+
876
+ return variantCss;
877
+ }
878
+
815
879
  // ============================================================================
816
880
  // DARK MODE VARIANTS
817
881
  // ============================================================================
@@ -983,6 +1047,65 @@ function generatePatternComponents() {
983
1047
  margin-inline: auto;
984
1048
  }
985
1049
 
1050
+ .prose-emily {
1051
+ max-width: 65ch;
1052
+ margin-inline: auto;
1053
+ }
1054
+
1055
+ .prose-emily > * + * {
1056
+ margin-top: var(--space-4, 1rem);
1057
+ }
1058
+
1059
+ .prose-emily h2,
1060
+ .prose-emily h3 {
1061
+ font-family: inherit;
1062
+ color: var(--color-neutral-90);
1063
+ line-height: 1.25;
1064
+ }
1065
+
1066
+ .prose-emily h2 {
1067
+ font-size: var(--text-2xl, 24px);
1068
+ margin-top: var(--space-10, 2.5rem);
1069
+ }
1070
+
1071
+ .prose-emily h3 {
1072
+ font-size: var(--text-xl, 20px);
1073
+ margin-top: var(--space-8, 2rem);
1074
+ }
1075
+
1076
+ .prose-emily p,
1077
+ .prose-emily li {
1078
+ color: var(--color-neutral-70);
1079
+ line-height: 1.75;
1080
+ }
1081
+
1082
+ .prose-emily ul,
1083
+ .prose-emily ol {
1084
+ padding-left: var(--space-6, 1.5rem);
1085
+ }
1086
+
1087
+ .prose-emily ul {
1088
+ list-style-type: disc;
1089
+ }
1090
+
1091
+ .prose-emily ol {
1092
+ list-style-type: decimal;
1093
+ }
1094
+
1095
+ .prose-emily a {
1096
+ color: var(--color-brand-80);
1097
+ text-decoration: underline;
1098
+ text-underline-offset: 2px;
1099
+ }
1100
+
1101
+ .prose-emily code {
1102
+ font-size: var(--text-sm, 14px);
1103
+ background-color: var(--color-neutral-10);
1104
+ border: 1px solid var(--color-neutral-20);
1105
+ border-radius: var(--space-1, 0.25rem);
1106
+ padding: 0.125rem 0.375rem;
1107
+ }
1108
+
986
1109
  /* ---- Composition ---- */
987
1110
 
988
1111
  /* Vertical stack with consistent gap — replaces manual margin chains */
@@ -1357,6 +1480,32 @@ function getProductionCssPath(config) {
1357
1480
  return path.join(process.cwd(), config.output?.css || 'dist/emily.min.css');
1358
1481
  }
1359
1482
 
1483
+ function getManifestSettings(config) {
1484
+ const manifestConfig = config.manifest;
1485
+
1486
+ if (manifestConfig === true) {
1487
+ return { enabled: true, output: 'dist/emily.manifest.json' };
1488
+ }
1489
+
1490
+ if (manifestConfig && typeof manifestConfig === 'object') {
1491
+ return {
1492
+ enabled: manifestConfig.enabled === true,
1493
+ output: manifestConfig.output || 'dist/emily.manifest.json',
1494
+ };
1495
+ }
1496
+
1497
+ return { enabled: false, output: 'dist/emily.manifest.json' };
1498
+ }
1499
+
1500
+ function getManifestOutputPath(config) {
1501
+ const manifestSettings = getManifestSettings(config);
1502
+ const outputPath = manifestSettings.output || 'dist/emily.manifest.json';
1503
+
1504
+ return path.isAbsolute(outputPath)
1505
+ ? outputPath
1506
+ : path.join(process.cwd(), outputPath);
1507
+ }
1508
+
1360
1509
  function ensureDirectoryForFile(filePath) {
1361
1510
  const dir = path.dirname(filePath);
1362
1511
 
@@ -1421,6 +1570,7 @@ function buildFullFramework() {
1421
1570
  utilityCss += filterUtilities();
1422
1571
 
1423
1572
  utilityCss = addStateVariants(utilityCss);
1573
+ utilityCss = addAriaDataVariants(utilityCss);
1424
1574
  utilityCss = addDarkModeVariants(utilityCss);
1425
1575
  utilityCss = addResponsiveVariants(utilityCss, config);
1426
1576
 
@@ -1577,6 +1727,16 @@ ${bodyFont}`;
1577
1727
  ensureDirectoryForFile(fullCssPath);
1578
1728
  fs.writeFileSync(fullCssPath, css);
1579
1729
 
1730
+ const manifestSettings = getManifestSettings(config);
1731
+ if (manifestSettings.enabled) {
1732
+ const manifestPath = getManifestOutputPath(config);
1733
+ const manifest = generateManifest(css, config);
1734
+
1735
+ ensureDirectoryForFile(manifestPath);
1736
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
1737
+ console.log(`✓ Generated manifest: ${manifestPath}`);
1738
+ }
1739
+
1580
1740
  console.log(`✓ Generated CSS: ${fullCssPath}`);
1581
1741
  console.log(`✓ File size: ${(css.length / 1024).toFixed(2)} KB (unminified)`);
1582
1742
  console.log('Full framework build complete');
@@ -1645,8 +1805,8 @@ function build(options = {}) {
1645
1805
  const fullCssPath = getFullCssPath(config);
1646
1806
  const result = buildProductionCss();
1647
1807
 
1648
- console.log(' Generated production CSS: ' + path.relative(process.cwd(), result.outputPath));
1649
- console.log(' File size: ' + (result.outputSize / 1024).toFixed(2) + ' KB');
1808
+ console.log('\u2713 Generated production CSS: ' + path.relative(process.cwd(), result.outputPath));
1809
+ console.log('\u2713 File size: ' + (result.outputSize / 1024).toFixed(2) + ' KB');
1650
1810
 
1651
1811
  if (!options.keepFull && fs.existsSync(fullCssPath)) {
1652
1812
  try {
@@ -1683,7 +1843,9 @@ module.exports = {
1683
1843
  generateFlexboxUtilities,
1684
1844
  generateGridUtilities,
1685
1845
  addStateVariants,
1846
+ addAriaDataVariants,
1686
1847
  addResponsiveVariants,
1848
+ generateManifest,
1687
1849
  generateFontCSS,
1688
1850
  codeUtilities,
1689
1851
  };
@@ -0,0 +1,307 @@
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
+ ];
21
+
22
+ function parseDeclarations(block) {
23
+ const declarations = {};
24
+ const declarationRegex = /([a-zA-Z-]+)\s*:\s*([^;]+)\s*;?/g;
25
+ let firstProperty = null;
26
+ let firstValue = null;
27
+ let match;
28
+
29
+ while ((match = declarationRegex.exec(block)) !== null) {
30
+ const property = match[1].trim();
31
+ const value = match[2].trim();
32
+ declarations[property] = value;
33
+
34
+ if (!firstProperty) {
35
+ firstProperty = property;
36
+ firstValue = value;
37
+ }
38
+ }
39
+
40
+ return { declarations, firstProperty, firstValue };
41
+ }
42
+
43
+ function getTokenFromDeclarations(declarations) {
44
+ const values = Object.values(declarations);
45
+
46
+ for (const value of values) {
47
+ const tokenMatch = value.match(/var\(\s*(--[a-zA-Z0-9-_]+)\s*(?:,[^)]+)?\)/);
48
+ if (tokenMatch) {
49
+ return tokenMatch[1];
50
+ }
51
+ }
52
+
53
+ return null;
54
+ }
55
+
56
+ function isSimpleBaseClassSelector(selector) {
57
+ if (!selector || !selector.startsWith('.')) return false;
58
+ if (selector.includes(' ')) return false;
59
+ if (selector.includes(',')) return false;
60
+ if (selector.includes('[')) return false;
61
+ if (selector.includes(':')) return false;
62
+ if (selector.includes('::')) return false;
63
+ if (selector.includes('>')) return false;
64
+ if (selector.includes('+')) return false;
65
+ if (selector.includes('~')) return false;
66
+
67
+ return true;
68
+ }
69
+
70
+ function inferCategory(className, property) {
71
+ if (
72
+ className === 'prose' ||
73
+ className === 'prose-emily' ||
74
+ className === 'center-screen' ||
75
+ className === 'center-absolute' ||
76
+ className === 'field-container' ||
77
+ className === 'form-hint' ||
78
+ className === 'form-error-message' ||
79
+ className === 'error-summary' ||
80
+ className === 'btn' ||
81
+ className === 'btn-primary' ||
82
+ className === 'btn-secondary' ||
83
+ className === 'btn-ghost' ||
84
+ className === 'btn-danger' ||
85
+ className === 'btn-sm' ||
86
+ className === 'btn-lg' ||
87
+ className === 'code-window' ||
88
+ className === 'code-title-bar' ||
89
+ className === 'code-dot' ||
90
+ className === 'code-dot-red' ||
91
+ className === 'code-dot-yellow' ||
92
+ className === 'code-dot-green' ||
93
+ className === 'code-filename' ||
94
+ className.startsWith('token-')
95
+ ) {
96
+ return 'component';
97
+ }
98
+
99
+ if (className.startsWith('text-')) {
100
+ return property === 'color' ? 'colour' : 'typography';
101
+ }
102
+
103
+ if (
104
+ className.startsWith('font-') ||
105
+ className.startsWith('leading-') ||
106
+ className.startsWith('tracking-') ||
107
+ className.startsWith('list-')
108
+ ) {
109
+ return 'typography';
110
+ }
111
+
112
+ if (className.startsWith('bg-')) return 'background';
113
+
114
+ if (
115
+ className === 'border' ||
116
+ className.startsWith('border-') ||
117
+ className.startsWith('divide-') ||
118
+ className === 'outline' ||
119
+ className.startsWith('outline-') ||
120
+ className === 'ring' ||
121
+ className.startsWith('ring-')
122
+ ) {
123
+ return 'border';
124
+ }
125
+
126
+ if (
127
+ className.startsWith('p-') ||
128
+ className.startsWith('px-') ||
129
+ className.startsWith('py-') ||
130
+ className.startsWith('pt-') ||
131
+ className.startsWith('pr-') ||
132
+ className.startsWith('pb-') ||
133
+ className.startsWith('pl-') ||
134
+ className.startsWith('m-') ||
135
+ className.startsWith('mx-') ||
136
+ className.startsWith('my-') ||
137
+ className.startsWith('mt-') ||
138
+ className.startsWith('mr-') ||
139
+ className.startsWith('mb-') ||
140
+ className.startsWith('ml-') ||
141
+ className.startsWith('gap-') ||
142
+ className.startsWith('space-') ||
143
+ className.startsWith('inset-') ||
144
+ className.startsWith('top-') ||
145
+ className.startsWith('right-') ||
146
+ className.startsWith('bottom-') ||
147
+ className.startsWith('left-')
148
+ ) {
149
+ return 'spacing';
150
+ }
151
+
152
+ if (
153
+ className.startsWith('w-') ||
154
+ className.startsWith('h-') ||
155
+ className.startsWith('min-w-') ||
156
+ className.startsWith('max-w-') ||
157
+ className.startsWith('min-h-') ||
158
+ className.startsWith('max-h-') ||
159
+ className.startsWith('size-')
160
+ ) {
161
+ return 'sizing';
162
+ }
163
+
164
+ if (
165
+ className === 'flex' ||
166
+ className === 'grid' ||
167
+ className === 'block' ||
168
+ className === 'inline' ||
169
+ className === 'inline-block' ||
170
+ className === 'inline-flex' ||
171
+ className === 'hidden' ||
172
+ className === 'container' ||
173
+ className === 'relative' ||
174
+ className === 'absolute' ||
175
+ className === 'fixed' ||
176
+ className === 'sticky' ||
177
+ className === 'static'
178
+ ) {
179
+ return 'layout';
180
+ }
181
+
182
+ if (
183
+ className.startsWith('items-') ||
184
+ className.startsWith('justify-') ||
185
+ className.startsWith('content-') ||
186
+ className.startsWith('self-') ||
187
+ className.startsWith('place-') ||
188
+ className.startsWith('order-') ||
189
+ className.startsWith('col-') ||
190
+ className.startsWith('row-')
191
+ ) {
192
+ return 'layout';
193
+ }
194
+
195
+ if (className === 'rounded' || className.startsWith('rounded-')) return 'radius';
196
+ if (className === 'shadow' || className.startsWith('shadow-')) return 'shadow';
197
+
198
+ if (
199
+ className.startsWith('opacity-') ||
200
+ className.startsWith('blur-') ||
201
+ className.startsWith('backdrop-') ||
202
+ className === 'filter' ||
203
+ className === 'grayscale' ||
204
+ className.startsWith('saturate-') ||
205
+ className.startsWith('brightness-') ||
206
+ className.startsWith('contrast-')
207
+ ) {
208
+ return 'effects';
209
+ }
210
+
211
+ if (
212
+ className === 'transition' ||
213
+ className.startsWith('transition-') ||
214
+ className.startsWith('duration-') ||
215
+ className.startsWith('ease-') ||
216
+ className.startsWith('delay-') ||
217
+ className.startsWith('animate-')
218
+ ) {
219
+ return 'motion';
220
+ }
221
+
222
+ if (
223
+ className === 'sr-only' ||
224
+ className === 'not-sr-only' ||
225
+ className === 'focus-ring' ||
226
+ className === 'skip-to-content' ||
227
+ className === 'js-hidden' ||
228
+ className === 'no-js'
229
+ ) {
230
+ return 'accessibility';
231
+ }
232
+
233
+ if (
234
+ className === 'input' ||
235
+ className === 'select' ||
236
+ className === 'textarea' ||
237
+ className === 'checkbox' ||
238
+ className === 'radio' ||
239
+ className === 'stack' ||
240
+ className === 'cluster' ||
241
+ className === 'width-container'
242
+ ) {
243
+ return 'component';
244
+ }
245
+
246
+ return 'utility';
247
+ }
248
+
249
+ function normalizeClassName(selector) {
250
+ return selector.slice(1).replace(/\\(.)/g, '$1');
251
+ }
252
+
253
+ function getVariants(config) {
254
+ const breakpoints =
255
+ config &&
256
+ config.breakpoints &&
257
+ typeof config.breakpoints === 'object' &&
258
+ Object.keys(config.breakpoints).length > 0
259
+ ? Object.keys(config.breakpoints)
260
+ : DEFAULT_RESPONSIVE_VARIANTS;
261
+
262
+ return [...BASE_VARIANTS, ...breakpoints];
263
+ }
264
+
265
+ function generateManifest(css, config = {}) {
266
+ const manifest = {
267
+ version: MANIFEST_VERSION,
268
+ generatedAt: new Date().toISOString(),
269
+ utilities: [],
270
+ };
271
+
272
+ if (typeof css !== 'string' || css.length === 0) {
273
+ return manifest;
274
+ }
275
+
276
+ const variants = getVariants(config);
277
+ const cleanedCss = css.replace(/\/\*[\s\S]*?\*\//g, '');
278
+ const ruleRegex = /([^{}]+)\{([^{}]*)\}/g;
279
+ let ruleMatch;
280
+
281
+ while ((ruleMatch = ruleRegex.exec(cleanedCss)) !== null) {
282
+ const selector = ruleMatch[1].trim();
283
+ const declarationBlock = ruleMatch[2].trim();
284
+
285
+ if (!isSimpleBaseClassSelector(selector)) continue;
286
+
287
+ const { declarations, firstProperty, firstValue } = parseDeclarations(declarationBlock);
288
+ if (!firstProperty) continue;
289
+
290
+ manifest.utilities.push({
291
+ class: normalizeClassName(selector),
292
+ category: inferCategory(normalizeClassName(selector), firstProperty),
293
+ property: firstProperty,
294
+ value: firstValue,
295
+ token: getTokenFromDeclarations(declarations),
296
+ declarations,
297
+ variants,
298
+ source: 'generated-css',
299
+ });
300
+ }
301
+
302
+ return manifest;
303
+ }
304
+
305
+ module.exports = {
306
+ generateManifest,
307
+ };
package/src/purge.js CHANGED
@@ -173,7 +173,7 @@ function purgeBlock(block, usedClasses) {
173
173
  .replace(/:/g, "\\\\:");
174
174
 
175
175
  const boundaryRegex = new RegExp(
176
- `\\.${escapedUsed}(?::[\\w\\-]+|[\\s,>+~]|$)`,
176
+ `\\.${escapedUsed}(?::[\\w\\-]+|\\[|[\\s,>+~]|$)`,
177
177
  );
178
178
 
179
179
  if (boundaryRegex.test(selector)) return true;
@@ -275,5 +275,5 @@ function purgeCSS(css, scanDir, config) {
275
275
  module.exports = {
276
276
  purgeCSS,
277
277
  getAllFiles,
278
- extractClassNames,
279
- };
278
+ extractClassNames
279
+ };