diff --git a/.changeset/cuddly-cougars-grow.md b/.changeset/cuddly-cougars-grow.md new file mode 100644 index 0000000000..a319be9775 --- /dev/null +++ b/.changeset/cuddly-cougars-grow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: strip trailing semicolons in style directive reactive updates diff --git a/packages/svelte/src/internal/client/dom/elements/style.js b/packages/svelte/src/internal/client/dom/elements/style.js index 740c225f29..91a3c7a261 100644 --- a/packages/svelte/src/internal/client/dom/elements/style.js +++ b/packages/svelte/src/internal/client/dom/elements/style.js @@ -1,3 +1,4 @@ +import { DEV } from 'esm-env'; import { to_style } from '../../../shared/attributes.js'; import { STYLE_CACHE } from '../../constants.js'; import { hydrating } from '../hydration.js'; @@ -13,10 +14,21 @@ function update_styles(dom, prev = {}, next, priority) { var value = next[key]; if (prev[key] !== value) { - if (next[key] == null) { + if (value == null) { dom.style.removeProperty(key); } else { - dom.style.setProperty(key, value, priority); + var str_value = String(value); + + if (DEV && /;+\s*$/.test(str_value)) { + // eslint-disable-next-line no-console + console.warn( + `[svelte] Style directive value for "${key}" has a trailing semicolon which is invalid and will be removed. Remove the trailing ";" from the value.` + ); + } + + // setProperty rejects values with trailing semicolons; strip them so that + // reactive updates behave consistently with the initial cssText assignment + dom.style.setProperty(key, str_value.replace(/;+\s*$/, ''), priority); } } } diff --git a/packages/svelte/tests/runtime-browser/samples/style-directive-trailing-semicolon/_config.js b/packages/svelte/tests/runtime-browser/samples/style-directive-trailing-semicolon/_config.js new file mode 100644 index 0000000000..1e96ec17f7 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/samples/style-directive-trailing-semicolon/_config.js @@ -0,0 +1,21 @@ +import { assert_ok, test } from '../../assert'; + +export default test({ + html: `
`, + + test({ assert, target, window, component }) { + const div = target.querySelector('div'); + assert_ok(div); + + // Initial value with trailing semicolon — should produce valid CSS + assert.equal(window.getComputedStyle(div).color, 'rgb(255, 0, 0)'); + + // Update to a new value that also has a trailing semicolon + component.color = 'blue;'; + assert.equal(window.getComputedStyle(div).color, 'rgb(0, 0, 255)'); + + // Update to a value without trailing semicolon — should still work + component.color = 'green'; + assert.equal(window.getComputedStyle(div).color, 'rgb(0, 128, 0)'); + } +}); diff --git a/packages/svelte/tests/runtime-browser/samples/style-directive-trailing-semicolon/main.svelte b/packages/svelte/tests/runtime-browser/samples/style-directive-trailing-semicolon/main.svelte new file mode 100644 index 0000000000..3ba2e22bc0 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/samples/style-directive-trailing-semicolon/main.svelte @@ -0,0 +1,5 @@ + + +