Describe the bug
Since v20.12.1, when a declaration value contains two or more var() references, only the first one is resolved. The remaining var() stays unresolved in the computed value. For shorthand properties (padding, margin, …) the partially resolved value then fails validation and the whole declaration is dropped, so getComputedStyle() returns "" for the shorthand and for all its longhands.
v20.12.0 resolves all of them correctly. This is a regression introduced by #2341 (the fix for #2256).
This breaks any MUI (Material UI) test suite that uses cssVariables: true: MUI's theme.spacing(a, b) emits calc(a * var(--mui-spacing)) calc(b * var(--mui-spacing)).
To Reproduce
import { Window } from "happy-dom";
const window = new Window();
const document = window.document;
document.body.innerHTML = `
<style>:root { --x: 16px; }</style>
<style>
.a { padding: calc(0.375 * var(--x)) calc(0.75 * var(--x)); }
.b { padding: var(--x) var(--x); }
.c { margin: calc(0.375 * var(--x)) calc(0.75 * var(--x)); }
.d { padding: calc(0.375 * var(--x)); }
</style>
<div class="a"></div><div class="b"></div><div class="c"></div><div class="d"></div>
`;
for (const cls of ["a", "b", "c", "d"]) {
const cs = window.getComputedStyle(document.querySelector("." + cls));
console.log(cls, JSON.stringify({ padding: cs.padding, paddingTop: cs.paddingTop, margin: cs.margin }));
}
Output with v20.12.1 / v20.12.2:
a {"padding":"","paddingTop":"","margin":""}
b {"padding":"16px var(--x)","paddingTop":"16px","margin":""}
c {"padding":"","paddingTop":"","margin":""}
d {"padding":"calc(0.375 * 16px)","paddingTop":"calc(0.375 * 16px)","margin":""}
Output with v20.12.0 (and v20.11.x):
a {"padding":"calc(0.375 * 16px) calc(0.75 * 16px)","paddingTop":"calc(0.375 * 16px)","margin":""}
b {"padding":"16px","paddingTop":"16px","margin":""}
c {"padding":"","paddingTop":"","margin":"calc(0.375 * 16px) calc(0.75 * 16px)"}
d {"padding":"calc(0.375 * 16px)","paddingTop":"calc(0.375 * 16px)","margin":""}
Case b shows the root cause directly: the second var(--x) is left untouched. The same happens with element.style.cssText = "padding: calc(0.375 * var(--x)) calc(0.75 * var(--x))" (the inline declaration is dropped as well).
Expected behavior
Every var() in the value is resolved, as in v20.12.0: .a → calc(0.375 * 16px) calc(0.75 * 16px), .b → 16px, .c margin → calc(0.375 * 16px) calc(0.75 * 16px).
Device and details:
- OS: macOS 26 (Darwin 25.5.0)
- Node version: 24.16.0
- Package version: 20.12.1, 20.12.2 (20.12.0 works)
Additional context
The cause is in CSSVariableFormatter.resolveVariables(): after the first var() is substituted, the tail of the string is appended verbatim instead of being resolved recursively. Applying resolveVariables to the tail fixes all cases above; with that one-line change our full test suite (~2000 tests, MUI v9 with CSS variables) passes on v20.12.2:
- return `${value.substring(0, variable.index)}${this.resolveVariables(variableValue || fallbackValue, cssVariables)}${value.substring(match.index! + match[0].length)}`;
+ return `${value.substring(0, variable.index)}${this.resolveVariables(variableValue || fallbackValue, cssVariables)}${this.resolveVariables(value.substring(match.index! + match[0].length), cssVariables)}`;
Describe the bug
Since v20.12.1, when a declaration value contains two or more
var()references, only the first one is resolved. The remainingvar()stays unresolved in the computed value. For shorthand properties (padding,margin, …) the partially resolved value then fails validation and the whole declaration is dropped, sogetComputedStyle()returns""for the shorthand and for all its longhands.v20.12.0 resolves all of them correctly. This is a regression introduced by #2341 (the fix for #2256).
This breaks any MUI (Material UI) test suite that uses
cssVariables: true: MUI'stheme.spacing(a, b)emitscalc(a * var(--mui-spacing)) calc(b * var(--mui-spacing)).To Reproduce
Output with v20.12.1 / v20.12.2:
Output with v20.12.0 (and v20.11.x):
Case
bshows the root cause directly: the secondvar(--x)is left untouched. The same happens withelement.style.cssText = "padding: calc(0.375 * var(--x)) calc(0.75 * var(--x))"(the inline declaration is dropped as well).Expected behavior
Every
var()in the value is resolved, as in v20.12.0:.a→calc(0.375 * 16px) calc(0.75 * 16px),.b→16px,.cmargin →calc(0.375 * 16px) calc(0.75 * 16px).Device and details:
Additional context
The cause is in
CSSVariableFormatter.resolveVariables(): after the firstvar()is substituted, the tail of the string is appended verbatim instead of being resolved recursively. ApplyingresolveVariablesto the tail fixes all cases above; with that one-line change our full test suite (~2000 tests, MUI v9 with CSS variables) passes on v20.12.2: