diff --git a/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts b/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts index c77fb160a..8e981ed8e 100644 --- a/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts +++ b/packages/happy-dom/src/css/declaration/element-style/CSSStyleDeclarationElementStyle.ts @@ -19,8 +19,8 @@ import CSSMeasurementConverter from '../measurement-converter/CSSMeasurementConv import MediaQueryList from '../../../match-media/MediaQueryList.js'; import WindowBrowserSettingsReader from '../../../window/WindowBrowserSettingsReader.js'; -const CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+)\)|var\( *(--[^), ]+), *([^), ]+)\)/; const CSS_MEASUREMENT_REGEXP = /[0-9.]+(px|rem|em|vw|vh|%|vmin|vmax|cm|mm|in|pt|pc|Q)/g; +const CSS_VARIABLE_REGEXP = /var\( *(--[^), ]+)\)|var\( *(--[^), ]+), *(.+)\)/; type IStyleAndElement = { element: Element | ShadowRoot | Document; @@ -387,7 +387,7 @@ export default class CSSStyleDeclarationElementStyle { */ private parseCSSVariablesInValue(value: string, cssVariables: { [k: string]: string }): string { let newValue = value; - let match; + let match: RegExpMatchArray | null; while ((match = newValue.match(CSS_VARIABLE_REGEXP)) !== null) { // Fallback value - E.g. var(--my-var, #FFFFFF) diff --git a/packages/happy-dom/test/window/BrowserWindow.test.ts b/packages/happy-dom/test/window/BrowserWindow.test.ts index 00a41b0fe..68ea609ae 100644 --- a/packages/happy-dom/test/window/BrowserWindow.test.ts +++ b/packages/happy-dom/test/window/BrowserWindow.test.ts @@ -592,11 +592,16 @@ describe('BrowserWindow', () => { color: var(--my-var, var(--my-background, pink)); --color1: red; + --color2: blue; --result1: var(--color1, var(--unknown, var(--unknown, green))); --result2: var(--unknown, var(--color1, var(--unknown, green))); --result3: var(--unknown, var(--unknown, var(--color1, green))); --result4: var(--unknown, var(--unknown, var(--unknown, var(--unknown, white)))); + + --result5: var(--color1, var(--color2)); + --result6: var(--unknown, var(--color2)); + --result7: var(--unknown, blue); } `; @@ -611,6 +616,57 @@ describe('BrowserWindow', () => { expect(computedStyle.getPropertyValue('--result2')).toBe('red'); expect(computedStyle.getPropertyValue('--result4')).toBe('white'); + + expect(computedStyle.getPropertyValue('--result5')).toBe('red'); + expect(computedStyle.getPropertyValue('--result6')).toBe('blue'); + expect(computedStyle.getPropertyValue('--result7')).toBe('blue'); + }); + + it('Returns values defined by a CSS variables when multiple chained variables are used.', () => { + const div = document.createElement('div'); + const divStyle = document.createElement('style'); + + divStyle.innerHTML = ` + div { + --my-var1: var(--my-var2); + --my-var2: var(--my-var3); + --my-var3: var(--my-var4); + --my-var4: pink; + + + --color1: red; + --color2: blue; + --case1-result1: var(--case1-result2); + --case1-result2: var(--case1-result3); + --case1-result3: var(--case1-result4); + --case1-result4: var(--color1, var(--color2)); + + --case2-result1: var(--case2-result2); + --case2-result2: var(--case2-result3); + --case2-result3: var(--case2-result4); + --case2-result4: var(--unknown, var(--color2)); + } + `; + + document.body.appendChild(divStyle); + document.body.appendChild(div); + + const computedStyle = window.getComputedStyle(div); + + expect(computedStyle.getPropertyValue('--my-var1')).toBe('pink'); + expect(computedStyle.getPropertyValue('--my-var2')).toBe('pink'); + expect(computedStyle.getPropertyValue('--my-var3')).toBe('pink'); + expect(computedStyle.getPropertyValue('--my-var4')).toBe('pink'); + + expect(computedStyle.getPropertyValue('--case1-result1')).toBe('red'); + expect(computedStyle.getPropertyValue('--case1-result2')).toBe('red'); + expect(computedStyle.getPropertyValue('--case1-result3')).toBe('red'); + expect(computedStyle.getPropertyValue('--case1-result4')).toBe('red'); + + expect(computedStyle.getPropertyValue('--case2-result1')).toBe('blue'); + expect(computedStyle.getPropertyValue('--case2-result2')).toBe('blue'); + expect(computedStyle.getPropertyValue('--case2-result3')).toBe('blue'); + expect(computedStyle.getPropertyValue('--case2-result4')).toBe('blue'); }); it('Returns a CSSStyleDeclaration object with computed styles containing "rem" and "em" measurement values converted to pixels.', () => {