@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.
- package/dist/cssPlugins/spx/calls.d.ts +5 -3
- package/dist/cssPlugins/spxRuleRefWidth/index.d.ts +6 -0
- package/dist/cssPlugins/spxRuleRefWidth/utils/rewriteUtils.d.ts +1 -0
- package/dist/cssPlugins/spxRuleRefWidth/utils/settingsUtils.d.ts +7 -0
- package/dist/cssPlugins/spxRuleRefWidth/utils/sourceUtils.d.ts +1 -0
- package/package.json +3 -3
- package/src/cssPlugins/index.js +4 -0
- package/src/cssPlugins/spx/README.md +19 -9
- package/src/cssPlugins/spx/calls.js +25 -15
- package/src/cssPlugins/spx/cascade.js +17 -12
- package/src/cssPlugins/spx/index.js +2 -1
- package/src/cssPlugins/spxRuleRefWidth/index.js +30 -0
- package/src/cssPlugins/spxRuleRefWidth/utils/rewriteUtils.js +23 -0
- package/src/cssPlugins/spxRuleRefWidth/utils/settingsUtils.js +47 -0
- package/src/cssPlugins/spxRuleRefWidth/utils/sourceUtils.js +18 -0
- package/src/cssPlugins/spxThemeVariables/README.md +3 -3
- package/src/cssPlugins/spxThemeVariables/utils/copyPlanUtils.js +2 -2
- package/src/cssPlugins/spxThemeVariables/utils/restateUtils.js +2 -3
- package/src/cssPlugins/spxThemeVariables/utils/themeUtils.js +2 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export const
|
|
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
|
|
4
|
-
export function
|
|
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.
|
|
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.
|
|
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": "
|
|
79
|
+
"falconPackageHash": "4250ddaa493ddd10cf58d5397ca1f88ce598f45125e41df45c22d2aa"
|
|
80
80
|
}
|
package/src/cssPlugins/index.js
CHANGED
|
@@ -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
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
##
|
|
71
|
+
## Layout calls
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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 `--
|
|
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
|
|
3
|
+
const LAYOUT_CALL = /--layout-spx\(\s*(-?(?:\d+\.?\d*|\.\d+))\s*,\s*(\d+\.?\d*|\.\d+)\s*\)/g
|
|
4
4
|
|
|
5
|
-
export const
|
|
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(
|
|
14
|
+
if (!declaration.value.includes(SPX)) return
|
|
14
15
|
declaration.value = declaration.value.replace(CALL, (_, literal) => resolve(Number(literal)))
|
|
15
|
-
if (declaration.value.includes(
|
|
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,
|
|
21
|
+
* @param {(value: number, layoutRefWidth: number) => string} resolve
|
|
21
22
|
* @param {() => Error} invalidCall
|
|
22
23
|
*/
|
|
23
|
-
export const
|
|
24
|
-
if (!declaration.value.includes(
|
|
25
|
-
declaration.value = declaration.value.replace(
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
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,
|
|
30
|
+
return resolve(number, refWidth)
|
|
30
31
|
})
|
|
31
|
-
if (declaration.value.includes(
|
|
32
|
+
if (declaration.value.includes(LAYOUT_SPX)) throw invalidCall()
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* @param {string} value
|
|
36
|
-
* @param {number}
|
|
37
|
+
* @param {string | number} layoutRefWidth
|
|
37
38
|
*/
|
|
38
|
-
export const
|
|
39
|
-
value.replace(CALL, (_, literal) => `--
|
|
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,
|
|
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
|
|
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
|
-
`(${
|
|
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
|
|
22
|
+
const compileLayoutCalls = declaration => {
|
|
19
23
|
const cap = declaration.prop === '--content-max-width' ? '99999px' : 'var(--content-max-width, 99999px)'
|
|
20
|
-
|
|
24
|
+
const floor = floorAt(LAYOUT_SCALE)
|
|
25
|
+
lowerLayoutCalls(
|
|
21
26
|
declaration,
|
|
22
|
-
(number,
|
|
27
|
+
(number, refWidth) => {
|
|
23
28
|
if (number === 0) return '0px'
|
|
24
|
-
const divisor = String(
|
|
25
|
-
const unit = unitCappedAt(cap, divisor)
|
|
26
|
-
return number < 0 ? `min(calc(-1 * ${
|
|
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 --
|
|
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
|
-
|
|
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(
|
|
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 `--
|
|
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 `--
|
|
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 `--
|
|
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 {
|
|
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(
|
|
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(
|
|
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 {
|
|
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 =
|
|
88
|
+
for (const declaration of variables) declaration.value = toLayoutCalls(declaration.value, refWidth)
|
|
89
89
|
}
|
|
90
90
|
}
|