@wix/origami-transforms 0.1.47 → 0.1.49

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.
@@ -1,4 +1,6 @@
1
- export const THEME_SPX: "--theme-spx(";
1
+ export const SPX: "--spx(";
2
+ export const LAYOUT_SPX: "--layout-spx(";
2
3
  export function lowerCalls(declaration: import("postcss").Declaration, resolve: (value: number) => string, invalidCall: () => Error): void;
3
- export function lowerThemeCalls(declaration: import("postcss").Declaration, resolve: (value: number, themeRefWidth: number) => string, invalidCall: () => Error): void;
4
- export function toThemeCalls(value: string, themeRefWidth: number): string;
4
+ export function lowerLayoutCalls(declaration: import("postcss").Declaration, resolve: (value: number, layoutRefWidth: number) => string, invalidCall: () => Error): void;
5
+ export function toLayoutCalls(value: string, layoutRefWidth: string | number): string;
6
+ export function toFixedCalls(value: string): string;
@@ -0,0 +1,6 @@
1
+ /** @typedef {typeof checks[number]} Check */
2
+ /** @type {import('../../transform/types.js').CssTransformPlugin<Check>} */
3
+ export const spxRuleRefWidthPlugin: import("../../transform/types.js").CssTransformPlugin<Check>;
4
+ export type Check = (typeof checks)[number];
5
+ declare const checks: readonly [];
6
+ export {};
@@ -0,0 +1 @@
1
+ export function resolveLayoutCalls(value: string, settings: import("./settingsUtils.js").SpxSettings): string;
@@ -0,0 +1,7 @@
1
+ export function enclosingRuleOf(node: import("postcss").Node): import("postcss").Rule | undefined;
2
+ export function readSpxSettings(rule: import("postcss").Rule): SpxSettings;
3
+ export function hasValidSettings({ refWidth, resolver }: SpxSettings): boolean;
4
+ export type SpxSettings = {
5
+ refWidth: string | undefined;
6
+ resolver: string | undefined;
7
+ };
@@ -0,0 +1 @@
1
+ export function isOwnRuleSource(node: import("postcss").Node, context: import("../../../transform/types.js").TransformContext): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/origami-transforms",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "description": "Babel and PostCSS checks and transforms for Wix origami page files.",
5
5
  "main": "./src/index.js",
6
6
  "files": [
@@ -24,7 +24,7 @@
24
24
  "@wix/origami-system-components": "1.10.12",
25
25
  "postcss": "^8.5.3",
26
26
  "postcss-custom-media": "^12.0.1",
27
- "@wix/origami-bi": "0.17.0"
27
+ "@wix/origami-bi": "0.18.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^22.0.0",
@@ -76,5 +76,5 @@
76
76
  },
77
77
  "type": "module",
78
78
  "types": "./dist/index.d.ts",
79
- "falconPackageHash": "3d57c6f49d1ce052c6e91ff5ef6e908c8eb281127ecbdb7fd3ea9924"
79
+ "falconPackageHash": "4250ddaa493ddd10cf58d5397ca1f88ce598f45125e41df45c22d2aa"
80
80
  }
@@ -11,6 +11,8 @@
11
11
  // authored one. custom-enums runs after mixins as well, so an enum a mixin body sets is resolved, and before presets
12
12
  // and layout move declarations out of the instance's rule. fonts appends the `@font-face` rules that load the Wix fonts
13
13
  // the stylesheet names from Wix's CDN.
14
+ // spx-rule-ref-width runs after mixins and custom enums and before presets and layout, so each layout.css and style.css
15
+ // rule's `--spx()` is fixed against that rule's own settings before declarations move to wrappers.
14
16
  // component-position runs after mixins, so a position a mixin body sets is kept, and before layout compiles the rules.
15
17
  // shape-dividers and wix-urls run last: they load the SVGs a stylesheet names from Wix's CDN in `prepare`, and write
16
18
  // them in as data URIs; wix-urls links a regular image a `wix:image` url names from Wix's media service, resized.
@@ -26,6 +28,7 @@ import { presetsPlugin } from './presets.js'
26
28
  import { shapeDividersPlugin } from './shapeDividers.js'
27
29
  import { layoutPlugin } from './layout/index.js'
28
30
  import { spxPlugin } from './spx/index.js'
31
+ import { spxRuleRefWidthPlugin } from './spxRuleRefWidth/index.js'
29
32
  import { spxThemeVariablesPlugin } from './spxThemeVariables/index.js'
30
33
  import { stackChildrenPlugin } from './stackChildren.js'
31
34
  import { wixUrlsPlugin } from './wixUrls.js'
@@ -37,6 +40,7 @@ export const cssPlugins = Object.freeze([
37
40
  mixinsPlugin,
38
41
  customEnumsPlugin,
39
42
  stackChildrenPlugin,
43
+ spxRuleRefWidthPlugin,
40
44
  presetsPlugin,
41
45
  componentPositionPlugin,
42
46
  layoutPlugin,
@@ -20,9 +20,10 @@ The desktop border declaration still applies on mobile. At a 390px viewport, it
20
20
  be 4px again because its reference width is now 390. If the build replaces `4 / 1280`
21
21
  with a constant, the mobile reference-width override can no longer affect that border.
22
22
 
23
- This applies to regular component styles as well as container layouts. There is one
24
- SPX conversion path for all declarations. Layout first moves content and overflow
25
- styles onto their runtime wrappers; this plugin then converts every `--spx(number)`.
23
+ This applies to any stylesheet except a site's own `layout.css` and `style.css`, whose rules
24
+ each use their own settings (see [Layout calls](#layout-calls)). There is one SPX conversion
25
+ path for all declarations. Layout first moves content and overflow styles onto their runtime
26
+ wrappers; this plugin then converts every `--spx(number)`.
26
27
 
27
28
  ## What the browser calculates
28
29
 
@@ -67,18 +68,27 @@ SPX expression resolves where the variable is declared; inheriting the variable
67
68
  re-evaluate it using a child's settings. So `../spxThemeVariables/` copies the SPX-dependent theme
68
69
  variables onto capped containers.
69
70
 
70
- ## Theme calls
71
+ ## Layout calls
71
72
 
72
- `../spxThemeVariables/` rewrites each theme `--spx(N)` as `--theme-spx(N, W)`, W being the theme's
73
- reference width; authors never write it. It compiles like `--spx(N)` but divides by the literal W
74
- instead of `var(--spx-ref-width, 1280)`, so a copied theme value keeps its theme size while still
75
- following the section's cap and scaling factor. N must be a literal number and W a positive one.
73
+ `--layout-spx(N, W)` is internal; authors never write it. It compiles like `--spx(N)` but divides by the literal W
74
+ instead of `var(--spx-ref-width, 1280)`, with the resolver switch fixed at 1. N must be a literal number and W a
75
+ positive one. Two plugins write it:
76
+
77
+ - **Theme:** `../spxThemeVariables/` rewrites each theme `--spx(N)` with W the theme's reference width, so a copied
78
+ theme value keeps its theme size while still following the section's cap and scaling factor. The theme `:root` is
79
+ `scale`, so the fixed switch matches its value there.
80
+ - **Own rule:** in a site-owned `layout.css` or `style.css`, `../spxRuleRefWidth/` settles each `--spx(N)` at build
81
+ time from the settings of its own rule (the rule holding the `@origami-target`, for target lines), as the live site
82
+ does. A value a mixin brings from another stylesheet keeps the cascade. A scale rule gets W from its
83
+ `--spx-ref-width` or 1280. A rule with no settings, or set to `fixed`, gets `Npx`. A rule with an invalid setting,
84
+ or a reference width that isn't a whole number, keeps its `--spx()` calls, so this plugin reports the setting's
85
+ check or compiles them as usual.
76
86
 
77
87
  ## Files and boundaries
78
88
 
79
89
  - `index.js` removes the SPX function definition and visits declarations.
80
90
  - `cascade.js` translates resolver settings and generates browser-evaluated expressions.
81
- - `calls.js` parses literal numeric `--spx()` and `--theme-spx()` calls and rejects unsupported arguments.
91
+ - `calls.js` parses literal numeric `--spx()` and `--layout-spx()` calls and rejects unsupported arguments.
82
92
 
83
93
  Reference widths must be positive numbers and resolver modes must be `scale` or `fixed`.
84
94
  Stoppers and `--content-max-width` are not validated.
@@ -1,8 +1,9 @@
1
1
  import { fail } from './index.js'
2
2
  const CALL = /--spx\(\s*(-?(?:\d+\.?\d*|\.\d+))\s*\)/g
3
- const THEME_CALL = /--theme-spx\(\s*(-?(?:\d+\.?\d*|\.\d+))\s*,\s*(\d+\.?\d*|\.\d+)\s*\)/g
3
+ const LAYOUT_CALL = /--layout-spx\(\s*(-?(?:\d+\.?\d*|\.\d+))\s*,\s*(\d+\.?\d*|\.\d+)\s*\)/g
4
4
 
5
- export const THEME_SPX = '--theme-spx('
5
+ export const SPX = '--spx('
6
+ export const LAYOUT_SPX = '--layout-spx('
6
7
 
7
8
  /** Replace literal SPX calls, reporting unsupported arguments at their declaration.
8
9
  * @param {import('postcss').Declaration} declaration
@@ -10,30 +11,39 @@ export const THEME_SPX = '--theme-spx('
10
11
  * @param {() => Error} invalidCall
11
12
  */
12
13
  export const lowerCalls = (declaration, resolve, invalidCall) => {
13
- if (!declaration.value.includes('--spx(')) return
14
+ if (!declaration.value.includes(SPX)) return
14
15
  declaration.value = declaration.value.replace(CALL, (_, literal) => resolve(Number(literal)))
15
- if (declaration.value.includes('--spx(')) throw fail('non_literal_spx_argument', invalidCall())
16
+ if (declaration.value.includes(SPX)) throw fail('non_literal_spx_argument', invalidCall())
16
17
  }
17
18
 
18
19
  /**
19
20
  * @param {import('postcss').Declaration} declaration
20
- * @param {(value: number, themeRefWidth: number) => string} resolve
21
+ * @param {(value: number, layoutRefWidth: number) => string} resolve
21
22
  * @param {() => Error} invalidCall
22
23
  */
23
- export const lowerThemeCalls = (declaration, resolve, invalidCall) => {
24
- if (!declaration.value.includes(THEME_SPX)) return
25
- declaration.value = declaration.value.replace(THEME_CALL, (call, literal, themeRefWidthLiteral) => {
26
- const themeRefWidth = Number(themeRefWidthLiteral)
27
- if (themeRefWidth <= 0) return call
24
+ export const lowerLayoutCalls = (declaration, resolve, invalidCall) => {
25
+ if (!declaration.value.includes(LAYOUT_SPX)) return
26
+ declaration.value = declaration.value.replace(LAYOUT_CALL, (call, literal, refWidthLiteral) => {
27
+ const refWidth = Number(refWidthLiteral)
28
+ if (refWidth <= 0) return call
28
29
  const number = Number(literal)
29
- return resolve(number, themeRefWidth)
30
+ return resolve(number, refWidth)
30
31
  })
31
- if (declaration.value.includes(THEME_SPX)) throw invalidCall()
32
+ if (declaration.value.includes(LAYOUT_SPX)) throw invalidCall()
32
33
  }
33
34
 
34
35
  /**
35
36
  * @param {string} value
36
- * @param {number} themeRefWidth
37
+ * @param {string | number} layoutRefWidth
37
38
  */
38
- export const toThemeCalls = (value, themeRefWidth) =>
39
- value.replace(CALL, (_, literal) => `--theme-spx(${literal}, ${themeRefWidth})`)
39
+ export const toLayoutCalls = (value, layoutRefWidth) =>
40
+ value.replace(CALL, (_, literal) => `--layout-spx(${literal}, ${layoutRefWidth})`)
41
+
42
+ /** @param {string} literal */
43
+ const toPixels = literal => {
44
+ const number = Number(literal)
45
+ return `${number}px`
46
+ }
47
+
48
+ /** @param {string} value */
49
+ export const toFixedCalls = value => value.replace(CALL, (_, literal) => toPixels(literal))
@@ -1,31 +1,36 @@
1
1
  import { fail } from './index.js'
2
- import { lowerCalls, lowerThemeCalls } from './calls.js'
2
+ import { lowerCalls, lowerLayoutCalls } from './calls.js'
3
3
 
4
4
  // Keep arithmetic at the call site: an inherited derived custom property would already
5
5
  // be computed against its parent's settings. Only the numeric resolver switch inherits.
6
6
  const SCALE = 'var(--origami-spx-scale, 1)'
7
- const FLOOR = `calc(0.5px * ${SCALE})`
7
+ const LAYOUT_SCALE = '1'
8
+ /** @param {string} scale */
9
+ const floorAt = scale => `calc(0.5px * ${scale})`
10
+ const FLOOR = floorAt(SCALE)
8
11
  /**
9
12
  * @param {string} cap
10
13
  * @param {string} [divisor]
14
+ * @param {string} [scale]
11
15
  */
12
- const unitCappedAt = (cap, divisor = 'var(--spx-ref-width, 1280)') =>
13
- `(${SCALE} * (clamp(var(--spx-stopper-min, 0px), var(--scaling-factor, 100vw), min(var(--spx-stopper-max, 99999px), ${cap})) - var(--scrollbar-width, 0px)) / ${divisor} + (1 - ${SCALE}) * 1px)`
16
+ const unitCappedAt = (cap, divisor = 'var(--spx-ref-width, 1280)', scale = SCALE) =>
17
+ `(${scale} * (clamp(var(--spx-stopper-min, 0px), var(--scaling-factor, 100vw), min(var(--spx-stopper-max, 99999px), ${cap})) - var(--scrollbar-width, 0px)) / ${divisor} + (1 - ${scale}) * 1px)`
14
18
  const UNIT = unitCappedAt('var(--content-max-width, 99999px)')
15
19
  const UNCAPPED_UNIT = unitCappedAt('99999px')
16
20
 
17
21
  /** @param {import('postcss').Declaration} declaration */
18
- const compileThemeCalls = declaration => {
22
+ const compileLayoutCalls = declaration => {
19
23
  const cap = declaration.prop === '--content-max-width' ? '99999px' : 'var(--content-max-width, 99999px)'
20
- lowerThemeCalls(
24
+ const floor = floorAt(LAYOUT_SCALE)
25
+ lowerLayoutCalls(
21
26
  declaration,
22
- (number, themeRefWidth) => {
27
+ (number, refWidth) => {
23
28
  if (number === 0) return '0px'
24
- const divisor = String(themeRefWidth)
25
- const unit = unitCappedAt(cap, divisor)
26
- return number < 0 ? `min(calc(-1 * ${FLOOR}), ${number} * ${unit})` : `max(${FLOOR}, ${number} * ${unit})`
29
+ const divisor = String(refWidth)
30
+ const unit = unitCappedAt(cap, divisor, LAYOUT_SCALE)
31
+ return number < 0 ? `min(calc(-1 * ${floor}), ${number} * ${unit})` : `max(${floor}, ${number} * ${unit})`
27
32
  },
28
- () => declaration.error('SPX calls --theme-spx() without a literal number and a positive literal theme reference width')
33
+ () => declaration.error('SPX calls --layout-spx() without a literal number and a positive literal reference width')
29
34
  )
30
35
  }
31
36
 
@@ -53,7 +58,7 @@ export const compileCascadeDeclaration = declaration => {
53
58
  ) {
54
59
  throw fail('invalid_spx_reference_width', declaration.error('SPX reference width must be positive'))
55
60
  }
56
- compileThemeCalls(declaration)
61
+ compileLayoutCalls(declaration)
57
62
  const unit = declaration.prop === '--content-max-width' ? UNCAPPED_UNIT : UNIT
58
63
  lowerCalls(
59
64
  declaration,
@@ -1,3 +1,4 @@
1
+ import { SPX } from './calls.js'
1
2
  import { compileCascadeDeclaration } from './cascade.js'
2
3
 
3
4
  export const checks = /** @type {const} */ (['unsupported_spx_resolver', 'invalid_spx_reference_width', 'non_literal_spx_argument'])
@@ -13,7 +14,7 @@ export const spxPlugin = {
13
14
  order: 20,
14
15
  transform(root) {
15
16
  root.walkAtRules('function', atRule => {
16
- if (atRule.params.trimStart().startsWith('--spx(')) atRule.remove()
17
+ if (atRule.params.trimStart().startsWith(SPX)) atRule.remove()
17
18
  })
18
19
  root.walkDecls(compileCascadeDeclaration)
19
20
  },
@@ -0,0 +1,30 @@
1
+ import { SPX } from '../spx/calls.js'
2
+ import { resolveLayoutCalls } from './utils/rewriteUtils.js'
3
+ import { enclosingRuleOf, hasValidSettings, readSpxSettings } from './utils/settingsUtils.js'
4
+ import { isOwnRuleSource } from './utils/sourceUtils.js'
5
+
6
+ /**
7
+ * @param {import('postcss').Declaration} declaration
8
+ * @param {import('../../transform/types.js').TransformContext} context
9
+ */
10
+ const rewriteDeclaration = (declaration, context) => {
11
+ if (!declaration.value.includes(SPX) || !isOwnRuleSource(declaration, context)) return
12
+ const rule = enclosingRuleOf(declaration)
13
+ if (rule === undefined) return
14
+ const settings = readSpxSettings(rule)
15
+ if (!hasValidSettings(settings)) return
16
+ declaration.value = resolveLayoutCalls(declaration.value, settings)
17
+ }
18
+
19
+ const checks = /** @type {const} */ ([])
20
+ /** @typedef {typeof checks[number]} Check */
21
+
22
+ /** @type {import('../../transform/types.js').CssTransformPlugin<Check>} */
23
+ export const spxRuleRefWidthPlugin = {
24
+ checks,
25
+ name: 'spx-rule-ref-width',
26
+ order: 12,
27
+ transform(root, context) {
28
+ root.walkDecls(declaration => rewriteDeclaration(declaration, context))
29
+ },
30
+ }
@@ -0,0 +1,23 @@
1
+ import { toFixedCalls, toLayoutCalls } from '../../spx/calls.js'
2
+
3
+ const DEFAULT_REF_WIDTH = '1280'
4
+ const DEFAULT_RESOLVER = 'scale'
5
+
6
+ /** @param {import('./settingsUtils.js').SpxSettings} settings */
7
+ const resolveSpxAs = ({ refWidth, resolver }) => resolver ?? (refWidth === undefined ? 'fixed' : DEFAULT_RESOLVER)
8
+
9
+ /**
10
+ * @param {string} value
11
+ * @param {import('./settingsUtils.js').SpxSettings} settings
12
+ */
13
+ const toScaledCalls = (value, { refWidth = DEFAULT_REF_WIDTH }) => toLayoutCalls(value, refWidth)
14
+
15
+ /**
16
+ * @param {string} value
17
+ * @param {import('./settingsUtils.js').SpxSettings} settings
18
+ */
19
+ export const resolveLayoutCalls = (value, settings) => {
20
+ const resolver = resolveSpxAs(settings)
21
+ if (resolver === 'fixed') return toFixedCalls(value)
22
+ return toScaledCalls(value, settings)
23
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @typedef {object} SpxSettings
3
+ * @property {string | undefined} refWidth
4
+ * @property {string | undefined} resolver
5
+ */
6
+
7
+ /**
8
+ * @param {import('postcss').Node} node
9
+ * @returns {import('postcss').Rule | undefined}
10
+ */
11
+ export const enclosingRuleOf = node => {
12
+ let current = node.parent
13
+ while (current !== undefined && current.type !== 'rule') current = current.parent
14
+ return current?.type === 'rule' ? /** @type {import('postcss').Rule} */ (current) : undefined
15
+ }
16
+
17
+ /**
18
+ * @param {import('postcss').Rule} rule
19
+ * @param {string} prop
20
+ */
21
+ const lastValueOf = (rule, prop) => {
22
+ const declarations = rule.nodes.filter(
23
+ /** @returns {node is import('postcss').Declaration} */ node => node.type === 'decl' && node.prop === prop
24
+ )
25
+ return declarations.at(-1)?.value.trim()
26
+ }
27
+
28
+ /**
29
+ * @param {import('postcss').Rule} rule
30
+ * @returns {SpxSettings}
31
+ */
32
+ export const readSpxSettings = rule => {
33
+ const refWidth = lastValueOf(rule, '--spx-ref-width')
34
+ const resolver = lastValueOf(rule, '--spx-resolver-type')
35
+ return { refWidth, resolver }
36
+ }
37
+
38
+ const WHOLE_NUMBER = /^\d+$/
39
+
40
+ /** @param {string | undefined} refWidth */
41
+ const isValidRefWidth = refWidth => refWidth === undefined || (WHOLE_NUMBER.test(refWidth) && Number(refWidth) > 0)
42
+
43
+ /** @param {string | undefined} resolver */
44
+ const isValidResolver = resolver => resolver === undefined || resolver === 'scale' || resolver === 'fixed'
45
+
46
+ /** @param {SpxSettings} settings */
47
+ export const hasValidSettings = ({ refWidth, resolver }) => isValidRefWidth(refWidth) && isValidResolver(resolver)
@@ -0,0 +1,18 @@
1
+ import { basename } from 'node:path'
2
+ import { relativeToSite } from '../../../transform/relativeToSite.js'
3
+
4
+ const OWN_RULE_FILES = new Set(['layout.css', 'style.css'])
5
+
6
+ /** @param {string} relative */
7
+ const isDependency = relative => relative.split(/[\\/]/).includes('node_modules')
8
+
9
+ /**
10
+ * @param {import('postcss').Node} node
11
+ * @param {import('../../../transform/types.js').TransformContext} context
12
+ */
13
+ export const isOwnRuleSource = (node, context) => {
14
+ const file = node.source?.input.file
15
+ if (!file || !OWN_RULE_FILES.has(basename(file))) return false
16
+ const relative = context.layout ? relativeToSite(context.layout.root, file) : file
17
+ return relative !== undefined && !isDependency(relative)
18
+ }
@@ -5,10 +5,10 @@ A custom property resolves `--spx()` where it is declared, so theme variables on
5
5
  - **Theme:** the `:root` rules from `src/global/themes/theme.css`, at the top level or under one `(max-width: Npx)` `@media`. Theme variables are their custom properties except the SPX settings. Without a site folder in the context, the plugin does nothing.
6
6
  - **Cascade:** at a breakpoint, the theme value of a variable is the one CSS gives `:root` there: among desktop, that breakpoint and every wider one, the declaration that comes last in the stylesheet wins, whatever the widths. So a wider block written after a narrower one overrides it.
7
7
  - **Restate:** a theme variable holding `--spx(` that a breakpoint inherits, through the cascade, from a breakpoint with another `--spx-ref-width` is re-declared on that breakpoint's last theme `:root`, widest breakpoint first, so it is tagged with the breakpoint's own reference width, like the `:root` value it had before. When a narrower breakpoint that sets the variable comes before that `:root`, the restatement goes in a new `:root` block of that breakpoint placed just before the narrower one, so the narrower value still wins. `var()` readers are not restated, and neither is a value written after that last `:root`, since the restatement would lose to it.
8
- - **Tag:** each theme `--spx(N)` becomes `--theme-spx(N, W)`, W being that breakpoint's `--spx-ref-width`, so a section's own reference width never changes it.
8
+ - **Tag:** each theme `--spx(N)` becomes `--layout-spx(N, W)`, W being that breakpoint's `--spx-ref-width`, so a section's own reference width never changes it.
9
9
  - **Capped:** a section whose desktop rule sets `--content-max-width`.
10
- - **Copied:** desktop copies every SPX-dependent variable (it holds `--theme-spx(` or reads one through `var()`, at any depth), except `--content-max-width`. Each breakpoint copies the variables whose cascade winner it sets that are SPX-dependent or override a wider copy, plus inherited ones that first become SPX-dependent there. Copies are written widest first, so each breakpoint copy holds the value `:root` has there.
10
+ - **Copied:** desktop copies every SPX-dependent variable (it holds `--layout-spx(` or reads one through `var()`, at any depth), except `--content-max-width`. Each breakpoint copies the variables whose cascade winner it sets that are SPX-dependent or override a wider copy, plus inherited ones that first become SPX-dependent there. Copies are written widest first, so each breakpoint copy holds the value `:root` has there.
11
11
  - **Written:** one `@layer origami-theme-copies` at the top, after any `@charset` and `@import`, so it is the lowest layer: any section rule wins, and a copy still beats the value inherited from `:root`. Inside it, one `:where(#a, #b, …)` rule for desktop, then one `@media` per breakpoint, widest first. Sections inside `@scope`, `@supports` or `@container` get their own list, wrapped in a copy of those wrapping at-rules, so the copies keep the section's condition. Copies drop `!important`. Nothing is written without copies or capped sections.
12
- - **Order:** 16, after layout (15) and before SPX (20), which compiles `--theme-spx()`.
12
+ - **Order:** 16, after layout (15) and before SPX (20), which compiles `--layout-spx()`.
13
13
 
14
14
  Code: `index.js` runs `stylesheetUtils.js` (scan), `themeUtils.js` (read), `copyPlanUtils.js` (breakpoint order, shared by restate and plan), `restateUtils.js`, `themeUtils.js` (tag), `sectionUtils.js` (with `wrappingAtRuleUtils.js`), `copyPlanUtils.js`, then `writeUtils.js`. Tests: `test/spxThemeVariables.spec.js`.
@@ -1,4 +1,4 @@
1
- import { THEME_SPX } from '../../spx/calls.js'
1
+ import { LAYOUT_SPX } from '../../spx/calls.js'
2
2
  import { cascadeAt, inheritedBreakpointsOf, themeBreakpointsOf, widestFirst } from './breakpointUtils.js'
3
3
  import { DESKTOP } from './cssTreeUtils.js'
4
4
  import { CONTENT_MAX_WIDTH } from './sectionUtils.js'
@@ -77,7 +77,7 @@ const collectReaders = (references, seeds) => {
77
77
 
78
78
  /** @param {VariableMap} variables */
79
79
  const findScaleDependent = variables => {
80
- const scaledVariables = [...variables].filter(([, declaration]) => declaration.value.includes(THEME_SPX))
80
+ const scaledVariables = [...variables].filter(([, declaration]) => declaration.value.includes(LAYOUT_SPX))
81
81
  const scaledProps = scaledVariables.map(([prop]) => prop)
82
82
  const scaled = new Set(scaledProps)
83
83
  const references = buildReferenceMap(variables)
@@ -1,4 +1,5 @@
1
1
  import { cascadeAt, maxWidthOf, refWidthAt } from './breakpointUtils.js'
2
+ import { SPX } from '../../spx/calls.js'
2
3
  import { isAtRule } from './cssTreeUtils.js'
3
4
 
4
5
  /** @typedef {import('./breakpointUtils.js').CascadedVariable} CascadedVariable */
@@ -12,8 +13,6 @@ import { isAtRule } from './cssTreeUtils.js'
12
13
  * @property {number} refWidth
13
14
  */
14
15
 
15
- const SPX_CALL = '--spx('
16
-
17
16
  /**
18
17
  * @param {ThemeVariables} theme
19
18
  * @param {string} breakpoint
@@ -31,7 +30,7 @@ const lastIndexAt = ({ rules }, breakpoint) => {
31
30
  const needsRestate = (theme, target, [, source]) => {
32
31
  if (source.breakpoint === target.breakpoint) return false
33
32
  if (source.index > target.index) return false
34
- if (!source.declaration.value.includes(SPX_CALL)) return false
33
+ if (!source.declaration.value.includes(SPX)) return false
35
34
  const sourceRefWidth = refWidthAt(theme, source.breakpoint)
36
35
  return sourceRefWidth !== target.refWidth
37
36
  }
@@ -1,4 +1,4 @@
1
- import { toThemeCalls } from '../../spx/calls.js'
1
+ import { toLayoutCalls } from '../../spx/calls.js'
2
2
  import { refWidthAt, resolveRefWidths } from './breakpointUtils.js'
3
3
  import { DESKTOP, isCustomProperty } from './cssTreeUtils.js'
4
4
 
@@ -85,6 +85,6 @@ export const tagThemeVariables = (themeRules, theme) => {
85
85
  for (const { breakpoint, declarations } of themeRules) {
86
86
  const refWidth = refWidthAt(theme, breakpoint)
87
87
  const variables = declarations.filter(({ prop }) => isThemeVariable(prop))
88
- for (const declaration of variables) declaration.value = toThemeCalls(declaration.value, refWidth)
88
+ for (const declaration of variables) declaration.value = toLayoutCalls(declaration.value, refWidth)
89
89
  }
90
90
  }