@pre.style/core 3.1.0-alpha.4 → 3.1.0-alpha.5

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/README.md CHANGED
@@ -922,13 +922,40 @@ Expanding means some shorthands produce many classes. For example, `font` also r
922
922
 
923
923
  ### Order
924
924
 
925
- Within one block, a later declaration overrides an earlier one, as in plain CSS. Across blocks, the CSS file is ordered so the usual rules hold:
925
+ Within one block, a later declaration overrides an earlier one, as in plain CSS (see [Fallbacks](#fallbacks) for the same property twice in a row). Across blocks, the CSS file is ordered so the usual rules hold:
926
926
 
927
927
  1. Hoisted at-rules (`@layer` statements, `@import`, `@namespace`), then other copied at-rules. With the `layer` option, everything but `@import` and `@namespace` is inside the layer.
928
928
  2. Classes on the element.
929
929
  3. Selector variants, with pseudo-classes in LVHFA order (`:link`, `:visited`, `:hover`, `:focus`, `:active`).
930
930
  4. At-rule groups. `@media` queries with only a `min-width` come last, smallest first (mobile-first).
931
931
 
932
+ ### Fallbacks
933
+
934
+ Declaring the same property more than once in a rule keeps the earlier values as fallbacks, for browsers that can't parse the later ones. They stay together in one class:
935
+
936
+ ```ts
937
+ css`
938
+ height: 100%;
939
+ height: 100svh;
940
+ `;
941
+ ```
942
+
943
+ ```css
944
+ .A {
945
+ height: 100%;
946
+ height: 100svh;
947
+ }
948
+ ```
949
+
950
+ Lightning CSS drops the fallbacks your [`targets`](#configuration) don't need, so set `targets` to keep them. Without `targets`, it assumes current browsers and writes only `height: 100svh` (which then shares a class with any other `height: 100svh`). Values it can't parse, such as `calc-size()`, always keep their fallbacks.
951
+
952
+ A few things aren't fallbacks:
953
+
954
+ - **A declaration in another rule**, such as `height: 100%; & { height: 100svh; }`, overrides the earlier one, since separate rules become separate classes.
955
+ - **A value with `var()`, `env()`, or `${…}`** replaces the declarations before it. Browsers only check these values when they're used, so they never fall back: an invalid value, or a `${…}` that is `null`, makes the property `unset`. To fall back from `${…}`, use a default (`height: var(${h}, 100%)`). A `${…}` followed by a static value is a real fallback (`height: ${h}; height: 100svh`).
956
+ - **Custom properties**, which accept any value.
957
+ - **Shorthands** are expanded into longhands, so `background: red; background: linear-gradient(…)` loses the `red`: the gradient's `background-color` (transparent) is a valid value in every browser.
958
+
932
959
  ## Compared to utility-first frameworks
933
960
 
934
961
  Utility-first frameworks such as [Tailwind CSS](https://tailwindcss.com/) also produce atomic CSS, so the same styles cost about the same in either. The differences are elsewhere:
@@ -1006,6 +1033,7 @@ preStyle({
1006
1033
  | --------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1007
1034
  | `lightningcss` | | [Lightning CSS options](https://lightningcss.dev/transforms.html), such as `minify`, `targets`, `drafts`, and `visitor`. A `visitor` runs before pre.style processes each block. `filename`, `code`, `sourceMap`, `projectRoot`, `inputSourceMap`, and `cssModules` are ignored. |
1008
1035
  | `sourceMap` | `true` in dev, `false` in builds | Generate a source map for the CSS, mapping each class to the first block it appears in (a source map can only point to one place; the [DevTools extension](#tracing-classes-with-the-devtools-extension) lists them all). In dev, it's shown in the browser (the plugin turns on Vite's `css.devSourcemap`, unless you set it). In builds, it's written next to `output.file`. When you import `virtual:pre.style.css`, builds have no CSS source map, because Vite doesn't write them for bundled CSS. |
1036
+ | `sourceAttributes` | `true` in dev, except in test runs | In dev, mark each `className`/`class` attribute in JSX and Astro components with where it's written (as `data-pre-style-at` on elements and `data-pre-style-from` on components), so the [DevTools extension](#tracing-classes-with-the-devtools-extension) can link an element to the line that set its classes. Needs `sourceMap`. Off by default in Vite's `test` mode (as in Vitest), so rendered markup and snapshots don't change; set it to `true` to have them there too. Builds never have the attributes. |
1009
1037
  | `layer` | `false` | Wrap the CSS in a [cascade layer](https://developer.mozilla.org/docs/Web/CSS/@layer): `false` for none, `""` for an unnamed layer, or the layer's name, such as `"pre.style"` or `"vendor.pre-style"`. `@import` and `@namespace` rules stay before the layer, since they can't be inside one. `@layer` rules from blocks become layers nested in it. Browsers without cascade layers (Chrome before 99, Safari before 15.4, Firefox before 97) ignore everything inside it. |
1010
1038
  | `quotes` | `'double-quote'` | Quotes around the class string that replaces each block in JS: `'single-quote'`, `'double-quote'`, or `'backtick'`. |
1011
1039
  | `output.file` | `'pre.style.css'` | Path and name of the CSS file, relative to Vite's `build.outDir`. |
@@ -1020,7 +1048,15 @@ Before 3.1, `include`, `exclude`, `prependFiles`, `libraries`, and `quirksMode`
1020
1048
 
1021
1049
  Classes are shared, so one class can come from many blocks, and a source map can only point to one of them. The [pre.style DevTools extension](packages/devtools) adds a **pre.style** tab to the Elements panel in Chrome and Firefox. For the selected element, it lists each pre.style rule like the Styles pane does, with a link to every block that uses it.
1022
1050
 
1023
- It works with the Vite dev server while `sourceMap` is on, which is the default in dev. Setting `sourceMap: false` turns it off. It only runs on `localhost`, `*.localhost`, loopback addresses, and hostnames you add in its settings, and sends other sites no requests.
1051
+ It works with the Vite dev server while `sourceMap` is on, which is the default in dev. Setting `sourceMap: false` turns it off.
1052
+
1053
+ It also links to where the element got its classes: the `className` or `class` attribute on the element, and the one on the component it was passed to, when the component passes its props on. For this, the plugin adds `data-pre-style-at` and `data-pre-style-from` attributes to your JSX and Astro components in dev (not in builds or test runs), which you'll see in the Elements panel. If you'd rather not see them, turn them off with the `sourceAttributes` option.
1054
+
1055
+ ```ts
1056
+ preStyle({ sourceAttributes: false });
1057
+ ```
1058
+
1059
+ It only runs on `localhost`, `*.localhost`, loopback addresses, and hostnames you add in its settings, and sends other sites no requests.
1024
1060
 
1025
1061
  Its links open your editor through Vite, which guesses which one you use. When the dev server runs in VS Code's terminal, the plugin sets the `LAUNCH_EDITOR` environment variable to `code` if it isn't set, which also applies to Vite's error overlay. To choose an editor yourself, set `LAUNCH_EDITOR`.
1026
1062
 
@@ -16,15 +16,17 @@ export interface Atom {
16
16
  key: string;
17
17
  selectors: SelectorList;
18
18
  } | null;
19
- declaration: {
20
- key: string;
21
- value: Declaration;
22
- important: boolean;
23
- };
19
+ declaration: AtomDeclaration;
24
20
  property: string;
25
21
  shorthand: boolean;
26
22
  position: Position | null;
27
23
  }
24
+ export interface AtomDeclaration {
25
+ key: string;
26
+ values: Declaration[];
27
+ important: boolean;
28
+ }
29
+ export declare const atomDeclaration: (values: Declaration[], important: boolean) => AtomDeclaration;
28
30
  export interface GlobalRule {
29
31
  key: string;
30
32
  rule: Rule;
@@ -77,6 +79,7 @@ export interface ParseOptions {
77
79
  export declare const key: (value: unknown) => string;
78
80
  export declare function parseBlock(code: string, lang: Lang, options: ParseOptions): ParsedBlock;
79
81
  export declare function parseRules(css: string, lightningcss: LightningOptions, position: ToPosition, collect: (rules: Rule[]) => void): void;
82
+ export declare function pruneFallbacks(atoms: Atom[], lightningcss: LightningOptions): Atom[];
80
83
  export declare function dropOverridden(atoms: Atom[]): Atom[];
81
84
  export type ToPosition = (loc: {
82
85
  line: number;
@@ -5,9 +5,16 @@ import { localizeCustomProperties } from './custom-properties.js';
5
5
  import { SLOT_PREFIX } from './interpolate.js';
6
6
  import { localizeKeyframes } from './keyframes.js';
7
7
  import { compose, fromPlaceholder, isElement, PLACEHOLDER, repair } from './selectors.js';
8
+ import { print } from './print.js';
8
9
  import { splitBlock } from './split.js';
9
10
  import { extractVariables } from './variables.js';
10
11
  import { SHORTHANDS } from '../shorthands.js';
12
+ // An atom's declaration. A single declaration's key is its own key.
13
+ export const atomDeclaration = (values, important) => ({
14
+ key: key(values.length === 1 ? values[0] : values) + (important ? '!' : ''),
15
+ values,
16
+ important,
17
+ });
11
18
  export class PreStyleError extends Error {
12
19
  position;
13
20
  constructor(message, position, options) {
@@ -86,7 +93,7 @@ export function parseBlock(code, lang, options) {
86
93
  // Sass output positions don't match the block, so scss atoms have none.
87
94
  const position = (loc) => lang === 'css' && loc ? toBlock(loc.line, loc.column) : null;
88
95
  parseRules(css, options.lightningcss, position, (rules) => walk(rules, [], null, parsed, position));
89
- let atoms = dropOverridden(parsed.atoms);
96
+ let atoms = dropOverridden(pruneFallbacks(parsed.atoms, options.lightningcss));
90
97
  let { globals } = parsed;
91
98
  if (options.customPropertyLocalization) {
92
99
  const local = localizeCustomProperties(atoms, globals, `block\n${code}`, lang, options);
@@ -127,6 +134,60 @@ export function parseRules(css, lightningcss, position, collect) {
127
134
  throw new PreStyleError(`Invalid CSS: ${error.message}`, loc ? position({ line: loc.line - 1, column: loc.column }) : null, { cause: error });
128
135
  }
129
136
  }
137
+ // Drops the fallbacks in each stack that Lightning CSS wouldn't print for the
138
+ // user's targets, so a stack that collapses to one declaration shares its
139
+ // class. Values Lightning CSS can't parse (such as `calc-size()`) are kept.
140
+ // Printing and parsing back changes some values' trees, so the round trip
141
+ // only decides which of the original declarations are kept.
142
+ export function pruneFallbacks(atoms, lightningcss) {
143
+ if (atoms.every((atom) => atom.declaration.values.length === 1))
144
+ return atoms;
145
+ const { visitor: _visitor, minify: _minify, ...options } = lightningcss;
146
+ // The stack as Lightning CSS prints it, parsed back, as keys. Declarations
147
+ // it adds for other properties (vendor prefixes) are left out.
148
+ const printed = (stack, important, property) => {
149
+ const block = important ? { importantDeclarations: stack } : { declarations: stack };
150
+ const rule = {
151
+ type: 'style',
152
+ value: {
153
+ selectors: [[{ type: 'class', name: 'x' }]],
154
+ declarations: block,
155
+ rules: [],
156
+ loc: { source_index: 0, line: 0, column: 1 },
157
+ },
158
+ };
159
+ let values = [];
160
+ parseRules(print([rule], [], options, false).code, options, () => null, (rules) => {
161
+ const parsed = valueOf(rules[0]).declarations ?? {};
162
+ values = (important ? parsed.importantDeclarations : parsed.declarations) ?? [];
163
+ });
164
+ return values.filter((value) => propertyOf(value).property === property).map(key);
165
+ };
166
+ return atoms.map((atom) => {
167
+ const { values: stack, important } = atom.declaration;
168
+ if (stack.length === 1)
169
+ return atom;
170
+ let kept;
171
+ try {
172
+ const remaining = printed(stack, important, atom.property);
173
+ if (remaining.length === stack.length)
174
+ return atom;
175
+ kept = stack.filter((value) => {
176
+ const own = printed([value], important, atom.property).at(-1);
177
+ if (own !== remaining[0])
178
+ return false;
179
+ remaining.shift();
180
+ return true;
181
+ });
182
+ }
183
+ catch {
184
+ return atom;
185
+ }
186
+ if (kept.length === 0)
187
+ return atom;
188
+ return { ...atom, declaration: atomDeclaration(kept, important) };
189
+ });
190
+ }
130
191
  // Within a block, a later declaration overrides an earlier one that sets the
131
192
  // same property under the same at-rules and selector variant, as in CSS.
132
193
  // `!important` declarations are collected after normal ones in each rule,
@@ -225,24 +286,43 @@ export function addDeclarations(block, atRules, selectors, atoms, composes, posi
225
286
  composes.push(parseComposes(d, position));
226
287
  normal = normal.slice(own);
227
288
  }
289
+ // Declarations of the same property in this list are stacked, in order, as
290
+ // fallbacks, at the place of the last one. A declaration that always parses
291
+ // replaces the stack, since nothing before it can ever apply.
228
292
  const add = (declarations = [], important) => {
293
+ const stacks = new Map();
294
+ const list = [];
229
295
  for (const declaration of declarations) {
230
296
  for (const value of expand(declaration)) {
231
297
  const { property, shorthand } = propertyOf(value);
232
- atoms.push({
233
- atRules,
234
- variant,
235
- declaration: { key: key(value) + (important ? '!' : ''), value, important },
236
- property,
237
- shorthand,
238
- position,
239
- });
298
+ const stack = stacks.get(property);
299
+ if (stack)
300
+ list.splice(list.findIndex((item) => item.values === stack), 1);
301
+ const values = stack && !alwaysParses(value, property) ? [...stack, value] : [value];
302
+ stacks.set(property, values);
303
+ list.push({ property, shorthand, values });
240
304
  }
241
305
  }
306
+ for (const { property, shorthand, values } of list)
307
+ atoms.push({
308
+ atRules,
309
+ variant,
310
+ declaration: atomDeclaration(values, important),
311
+ property,
312
+ shorthand,
313
+ position,
314
+ });
242
315
  };
243
316
  add(normal, false);
244
317
  add(block?.importantDeclarations, true);
245
318
  }
319
+ // Whether a browser always parses a declaration, so it can't fall back to an
320
+ // earlier one: custom properties take any value, and `var()` and `env()`
321
+ // (including `${…}`, which becomes a `var()`) are only checked once they're
322
+ // substituted, when an invalid value makes the property `unset`.
323
+ function alwaysParses(value, property) {
324
+ return property.startsWith('--') || /"type":"(?:var|env)"/.test(key(value));
325
+ }
246
326
  const ident = (t) => t?.type === 'token' && t.value.type === 'ident' ? t.value.value : null;
247
327
  // Reads `composes: a b`, `composes: a from './file.css'`, or
248
328
  // `composes: a from global`.
@@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs';
2
2
  import { dirname, relative, resolve as resolvePath, sep } from 'node:path';
3
3
  import { transform } from 'lightningcss';
4
4
  import SweatMap from 'sweatmap';
5
- import { dropOverridden, parseBlock, PreStyleError, valueOf } from './block.js';
5
+ import { atomDeclaration, dropOverridden, parseBlock, PreStyleError, valueOf } from './block.js';
6
6
  import { interpolate } from './interpolate.js';
7
7
  import { print } from './print.js';
8
8
  import { lvhfaRank, specificity, withClass } from './selectors.js';
@@ -544,12 +544,12 @@ export class Compiler {
544
544
  }
545
545
  #atomRule(record, loc) {
546
546
  const { atom } = record;
547
- const value = hasGenerated(atom.declaration.key)
548
- ? this.#named(atom.declaration.value)
549
- : atom.declaration.value;
547
+ const values = hasGenerated(atom.declaration.key)
548
+ ? this.#named(atom.declaration.values)
549
+ : atom.declaration.values;
550
550
  const declarations = atom.declaration.important
551
- ? { importantDeclarations: [value] }
552
- : { declarations: [value] };
551
+ ? { importantDeclarations: values }
552
+ : { declarations: values };
553
553
  return {
554
554
  type: 'style',
555
555
  value: { selectors: this.#atomSelectors(record), declarations, rules: [], loc },
@@ -574,6 +574,20 @@ export class Compiler {
574
574
  };
575
575
  return emit(this.#atomGroups());
576
576
  }
577
+ // An atom's rule as text. Without the user's targets, Lightning CSS would
578
+ // drop a stack's fallbacks, so each declaration is printed on its own.
579
+ #printedStack(record, rule, text) {
580
+ const code = text(rule);
581
+ const { values, important } = record.atom.declaration;
582
+ if (values.length === 1)
583
+ return code;
584
+ const lines = values.map((value) => {
585
+ const atom = { ...record.atom, declaration: atomDeclaration([value], important) };
586
+ const own = text(this.#atomRule({ ...record, atom }, valueOf(rule).loc));
587
+ return own.slice(own.indexOf(' {\n') + 3, own.lastIndexOf('\n}'));
588
+ });
589
+ return `${code.slice(0, code.indexOf(' {\n'))} {\n${lines.join('\n')}\n}`;
590
+ }
577
591
  #usage(record, order) {
578
592
  const { atom } = record;
579
593
  const loc = { source_index: 0, line: 0, column: 1 };
@@ -581,7 +595,7 @@ export class Compiler {
581
595
  // Printed for reading, so without the user's `minify` and `targets`.
582
596
  const { drafts, nonStandard, errorRecovery } = this.#parseOptions.lightningcss;
583
597
  const text = (rules) => print([rules], [], { drafts, nonStandard, errorRecovery }, false).code;
584
- const code = (record.printed ??= text(rule));
598
+ const code = (record.printed ??= this.#printedStack(record, rule, text));
585
599
  const open = code.indexOf(' {\n');
586
600
  return {
587
601
  className: record.className,
@@ -595,7 +609,9 @@ export class Compiler {
595
609
  declaration: code
596
610
  .slice(open + 3, code.lastIndexOf('\n}'))
597
611
  .trim()
598
- .replace(/;$/, ''),
612
+ .replace(/;$/, '')
613
+ // Fallbacks, on one line.
614
+ .replaceAll(/;\n\s*/g, '; '),
599
615
  property: this.#propertyName(atom.property),
600
616
  covers: Object.hasOwn(SHORTHANDS, atom.property) ? SHORTHANDS[atom.property] : [],
601
617
  important: atom.declaration.important,
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { key, overrideKey, parseRules, valueOf, walk } from './block.js';
2
+ import { atomDeclaration, key, overrideKey, parseRules, valueOf, walk } from './block.js';
3
3
  import { VARIABLE_PREFIX } from './variables.js';
4
4
  // With `customPropertyLocalization`, a custom property that a block or
5
5
  // stylesheet file declares gets a generated name, used only in that block or
@@ -103,7 +103,7 @@ export function localizeCustomProperties(atoms, globals, scope, lang, options) {
103
103
  local.set(name, VARIABLE_PREFIX + hash);
104
104
  };
105
105
  for (const atom of atoms)
106
- eachDeclared(atom.declaration.value, declared);
106
+ eachDeclared(atom.declaration.values, declared);
107
107
  const own = (global) => !shared.keys.has(global.key);
108
108
  for (const global of globals)
109
109
  if (own(global))
@@ -125,23 +125,19 @@ export function localizeCustomProperties(atoms, globals, scope, lang, options) {
125
125
  };
126
126
  }),
127
127
  atoms: (list) => list.map((atom) => {
128
- const value = rename(atom.declaration.value, local);
128
+ const values = rename(atom.declaration.values, local);
129
129
  const atRules = atom.atRules.map((segment) => {
130
130
  const rule = rename(segment.rule, local);
131
131
  return rule === segment.rule ? segment : { key: key(valueKey(rule)), rule };
132
132
  });
133
- const unchanged = value === atom.declaration.value && atRules.every((s, i) => s === atom.atRules[i]);
133
+ const unchanged = values === atom.declaration.values && atRules.every((s, i) => s === atom.atRules[i]);
134
134
  if (unchanged)
135
135
  return atom;
136
136
  return {
137
137
  ...atom,
138
138
  atRules,
139
139
  property: local.get(atom.property) ?? atom.property,
140
- declaration: {
141
- ...atom.declaration,
142
- key: key(value) + (atom.declaration.important ? '!' : ''),
143
- value,
144
- },
140
+ declaration: atomDeclaration(values, atom.declaration.important),
145
141
  };
146
142
  }),
147
143
  };
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { key, overrideKey, valueOf } from './block.js';
2
+ import { atomDeclaration, key, overrideKey, valueOf } from './block.js';
3
3
  // With `keyframeLocalization`, each `@keyframes` name is replaced with a
4
4
  // generated one, like styled-components' `keyframes`. In parsed rules, a
5
5
  // local name is an ident with this prefix and a hash of every definition of
@@ -55,17 +55,10 @@ export function localizeKeyframes(globals) {
55
55
  atoms: (atoms) => atoms.map((atom) => {
56
56
  if (!ANIMATION_PROPERTIES.has(atom.property.replace(/^-\w+-/, '')))
57
57
  return atom;
58
- const value = renameIdents(atom.declaration.value, local);
59
- if (value === atom.declaration.value)
58
+ const values = renameIdents(atom.declaration.values, local);
59
+ if (values === atom.declaration.values)
60
60
  return atom;
61
- return {
62
- ...atom,
63
- declaration: {
64
- ...atom.declaration,
65
- key: key(value) + (atom.declaration.important ? '!' : ''),
66
- value,
67
- },
68
- };
61
+ return { ...atom, declaration: atomDeclaration(values, atom.declaration.important) };
69
62
  }),
70
63
  };
71
64
  }
@@ -1,6 +1,6 @@
1
1
  import { pathToFileURL, fileURLToPath } from 'node:url';
2
2
  import { atRuleInfo } from './at-rules.js';
3
- import { addDeclarations, addGlobal, dropOverridden, key, parseRules, PreStyleError, valueOf, walk, } from './block.js';
3
+ import { addDeclarations, addGlobal, dropOverridden, pruneFallbacks, key, parseRules, PreStyleError, valueOf, walk, } from './block.js';
4
4
  import { localizeCustomProperties } from './custom-properties.js';
5
5
  import { localizeKeyframes } from './keyframes.js';
6
6
  import { compose, isElement } from './selectors.js';
@@ -145,7 +145,7 @@ export function parseStylesheet(code, lang, file, id, options) {
145
145
  }
146
146
  }
147
147
  for (const target of classes.values())
148
- target.atoms = dropOverridden(target.atoms);
148
+ target.atoms = dropOverridden(pruneFallbacks(target.atoms, options.lightningcss));
149
149
  // Custom properties are local to the whole file, like keyframes.
150
150
  if (options.customPropertyLocalization) {
151
151
  const all = [...classes.values()].flatMap((target) => target.atoms);
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { transform } from 'lightningcss';
3
- import { key, overrideKey, PreStyleError, valueOf } from './block.js';
3
+ import { atomDeclaration, key, overrideKey, PreStyleError, valueOf } from './block.js';
4
4
  import { atRuleInfo } from './at-rules.js';
5
5
  import { SLOT_PREFIX } from './interpolate.js';
6
6
  import { print } from './print.js';
@@ -43,7 +43,9 @@ const IGNORED = (token) => token.type === 'token' && token.value?.type === 'whit
43
43
  function directSlot(atom) {
44
44
  if (atom.atRules.length > 0 || atom.variant !== null || atom.declaration.important)
45
45
  return null;
46
- const declaration = atom.declaration.value;
46
+ if (atom.declaration.values.length !== 1)
47
+ return null;
48
+ const declaration = atom.declaration.values[0];
47
49
  // Lightning CSS also uses `custom` for properties it doesn't parse, such as
48
50
  // `content` with a `var()`.
49
51
  if (declaration.property !== 'custom' || !atom.property.startsWith('--'))
@@ -251,22 +253,17 @@ export function extractVariables(atoms, globals) {
251
253
  }
252
254
  continue;
253
255
  }
254
- const value = structuredClone(atom.declaration.value);
256
+ const values = structuredClone(atom.declaration.values);
255
257
  const selector = selectorOf(atom);
256
258
  let index = 0;
257
- eachSlot(value, (node) => {
259
+ // Counted across the whole stack, so `height: ${a}; height: ${b}` is two
260
+ // variables.
261
+ eachSlot(values, (node) => {
258
262
  const { slot, syntax } = parseSlot(node);
259
263
  const identity = atomIdentity(atom, index++);
260
264
  useVariable(node, identity, slot, syntax, variables, added, atom.position, selector);
261
265
  });
262
- keptAtoms.push({
263
- ...atom,
264
- declaration: {
265
- ...atom.declaration,
266
- key: key(value) + (atom.declaration.important ? '!' : ''),
267
- value,
268
- },
269
- });
266
+ keptAtoms.push({ ...atom, declaration: atomDeclaration(values, atom.declaration.important) });
270
267
  }
271
268
  return { atoms: keptAtoms, globals: [...kept, ...added.values()], variables };
272
269
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pre.style/core",
3
- "version": "3.1.0-alpha.4",
3
+ "version": "3.1.0-alpha.5",
4
4
  "description": "Zero-runtime CSS-in-JS that compiles css and scss blocks into atomic CSS classes.",
5
5
  "keywords": [
6
6
  "atomic-css",