diff --git a/.changeset/fix-svg-classname.md b/.changeset/fix-svg-classname.md
new file mode 100644
index 0000000000..59e792df11
--- /dev/null
+++ b/.changeset/fix-svg-classname.md
@@ -0,0 +1,6 @@
+---
+"@marko/runtime-tags": patch
+"marko": patch
+---
+
+Fix setting `class` on SVG elements throwing `TypeError: setting getter-only property "className"`
diff --git a/.sizes.json b/.sizes.json
index 2e0e4b44d5..1afe36f060 100644
--- a/.sizes.json
+++ b/.sizes.json
@@ -7,8 +7,8 @@
{
"name": "*",
"total": {
- "min": 21166,
- "brotli": 7935
+ "min": 21155,
+ "brotli": 7926
}
},
{
diff --git a/.sizes/dom.js b/.sizes/dom.js
index 6080a66618..2b2087039f 100644
--- a/.sizes/dom.js
+++ b/.sizes/dom.js
@@ -1,4 +1,4 @@
-// size: 21166 (min) 7935 (brotli)
+// size: 21155 (min) 7926 (brotli)
//#region packages/runtime-tags/dist/dom.mjs
let empty = [],
rest = Symbol(),
@@ -1270,11 +1270,13 @@ function setAttribute(element, name, value) {
: element.setAttribute(name, value));
}
function _attr_class(element, value) {
- ((value =
- typeof value == "string"
+ setAttribute(
+ element,
+ "class",
+ (typeof value == "string"
? value
- : toDelimitedString(value, " ", stringifyClassObject)),
- value !== element.className && (element.className = value));
+ : toDelimitedString(value, " ", stringifyClassObject)) || void 0,
+ );
}
function _attr_class_items(element, items) {
for (let key in items) _attr_class_item(element, key, items[key]);
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/html.bundle.debug.js b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/html.bundle.debug.js
new file mode 100644
index 0000000000..67030923c2
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/html.bundle.debug.js
@@ -0,0 +1,8 @@
+// template.marko
+var template_default = _template("__tests__/template.marko", (input) => {
+ const $scope0_reason = _scope_reason(), $sg__input_active = _serialize_guard($scope0_reason, 0);
+ const $scope0_id = _scope_id();
+ const { active } = input;
+ _html(`${_el_resume($scope0_id, "#svg/0", $sg__input_active)}`);
+ _serialize_if($scope0_reason, 0) && writeScope($scope0_id, {}, "__tests__/template.marko", 0);
+}, 1);
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/html.bundle.js b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/html.bundle.js
new file mode 100644
index 0000000000..9324473fa1
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/html.bundle.js
@@ -0,0 +1,8 @@
+// template.marko
+var template_default = _template("a", (input) => {
+ const $scope0_reason = _scope_reason(), $sg__input_active = _serialize_guard($scope0_reason, 0);
+ const $scope0_id = _scope_id();
+ const { active } = input;
+ _html(`${_el_resume($scope0_id, "a", $sg__input_active)}`);
+ _serialize_if($scope0_reason, 0) && writeScope($scope0_id, {});
+}, 1);
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-csr.debug.md b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-csr.debug.md
new file mode 100644
index 0000000000..239a6a8460
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-csr.debug.md
@@ -0,0 +1,51 @@
+# Render `{"active":false}`
+```html
+
+```
+
+# Update `{"active":true}`
+```html
+
+```
+## Change
+```
+UPDATE: .icon.active[class] "icon" => "icon active"
+UPDATE: .on[class] "off" => "on"
+```
+
+# Update `{"active":false}`
+```html
+
+```
+## Change
+```
+UPDATE: .icon[class] "icon active" => "icon"
+UPDATE: .off[class] "on" => "off"
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-csr.md b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-csr.md
new file mode 100644
index 0000000000..239a6a8460
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-csr.md
@@ -0,0 +1,51 @@
+# Render `{"active":false}`
+```html
+
+```
+
+# Update `{"active":true}`
+```html
+
+```
+## Change
+```
+UPDATE: .icon.active[class] "icon" => "icon active"
+UPDATE: .on[class] "off" => "on"
+```
+
+# Update `{"active":false}`
+```html
+
+```
+## Change
+```
+UPDATE: .icon[class] "icon active" => "icon"
+UPDATE: .off[class] "on" => "off"
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-ssr.debug.md b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-ssr.debug.md
new file mode 100644
index 0000000000..285b99ced8
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-ssr.debug.md
@@ -0,0 +1,13 @@
+# Render `{"active":false}`
+```html
+
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-ssr.md b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-ssr.md
new file mode 100644
index 0000000000..285b99ced8
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/render-ssr.md
@@ -0,0 +1,13 @@
+# Render `{"active":false}`
+```html
+
+```
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/writes.debug.html b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/writes.debug.html
new file mode 100644
index 0000000000..ba9a15b3bb
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/writes.debug.html
@@ -0,0 +1,3 @@
+
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/writes.html b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/writes.html
new file mode 100644
index 0000000000..48cfd66efb
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/__snapshots__/writes.html
@@ -0,0 +1,4 @@
+
+
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/template.marko b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/template.marko
new file mode 100644
index 0000000000..1028b4db06
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/template.marko
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/test.ts b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/test.ts
new file mode 100644
index 0000000000..22aa504cfa
--- /dev/null
+++ b/packages/runtime-tags/src/__tests__/fixtures/attr-class-svg/test.ts
@@ -0,0 +1,6 @@
+import type { TestConfig } from "../../main.test";
+
+export const config: TestConfig = {
+ equivalent: false,
+ steps: [{ active: false }, { active: true }, { active: false }],
+};
diff --git a/packages/runtime-tags/src/__tests__/fixtures/multi-class-toggle/__snapshots__/dom.bundle.js b/packages/runtime-tags/src/__tests__/fixtures/multi-class-toggle/__snapshots__/dom.bundle.js
index d609276b6d..2609b95c61 100644
--- a/packages/runtime-tags/src/__tests__/fixtures/multi-class-toggle/__snapshots__/dom.bundle.js
+++ b/packages/runtime-tags/src/__tests__/fixtures/multi-class-toggle/__snapshots__/dom.bundle.js
@@ -1,4 +1,4 @@
-// total: 2834 (min) 1454 (brotli)
+// total: 2916 (min) 1480 (brotli)
// template.marko: 144 (min) 130 (brotli)
const $count__script = _script("a0", ($scope) => _on($scope.a, "click", function() {
$count($scope, $scope.c + 1);
diff --git a/packages/runtime-tags/src/dom/dom.ts b/packages/runtime-tags/src/dom/dom.ts
index 8958dad7ab..3ab9b02f2c 100644
--- a/packages/runtime-tags/src/dom/dom.ts
+++ b/packages/runtime-tags/src/dom/dom.ts
@@ -59,13 +59,11 @@ function setAttribute(
}
export function _attr_class(element: Element, value: unknown) {
- value =
- typeof value === "string"
- ? value
- : toDelimitedString(value, " ", stringifyClassObject);
- if (value !== element.className) {
- element.className = value as string;
- }
+ setAttribute(
+ element,
+ "class",
+ toDelimitedString(value, " ", stringifyClassObject) || undefined,
+ );
}
export function _attr_class_items(