${$.escape(title)}
we don't need to traverse these nodes
or
these
ones
${$.html(content)}these
trailing
nodes
can
be
completely
ignored
diff --git a/.changeset/tired-cities-wink.md b/.changeset/tired-cities-wink.md
new file mode 100644
index 0000000000..a319b5b2b0
--- /dev/null
+++ b/.changeset/tired-cities-wink.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: render boolean attribute values as empty strings for XHTML compliance
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
index 720522beaf..f651586dd8 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js
@@ -256,10 +256,7 @@ export function RegularElement(node, context) {
}
if (name !== 'class' || value) {
- context.state.template.set_prop(
- attribute.name,
- is_boolean_attribute(name) && value === true ? undefined : value === true ? '' : value
- );
+ context.state.template.set_prop(attribute.name, value === true ? '' : value);
}
} else if (name === 'autofocus') {
let { value } = build_attribute_value(attribute.value, context);
diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js
index 979efef6b9..f4f491c056 100644
--- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js
+++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js
@@ -235,13 +235,7 @@ export function build_element_attributes(node, context, transform) {
if (name !== 'class' || literal_value) {
context.state.template.push(
- b.literal(
- ` ${attribute.name}${
- is_boolean_attribute(name) && literal_value === true
- ? ''
- : `="${literal_value === true ? '' : String(literal_value)}"`
- }`
- )
+ b.literal(` ${attribute.name}="${literal_value === true ? '' : String(literal_value)}"`)
);
}
diff --git a/packages/svelte/tests/runtime-xhtml/samples/boolean-attributes/_config.js b/packages/svelte/tests/runtime-xhtml/samples/boolean-attributes/_config.js
index f965e04b02..f47bee71df 100644
--- a/packages/svelte/tests/runtime-xhtml/samples/boolean-attributes/_config.js
+++ b/packages/svelte/tests/runtime-xhtml/samples/boolean-attributes/_config.js
@@ -1,5 +1,3 @@
import { test } from '../../test';
-export default test({
- skip: true
-});
+export default test({});
diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js
index 7a9f6193d7..b1babb514c 100644
--- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js
@@ -3,7 +3,7 @@ import * as $ from 'svelte/internal/server';
export default function Skip_static_subtree($$renderer, $$props) {
let { title, content } = $$props;
- $$renderer.push(` we don't need to traverse these nodes or these ones these trailing nodes can be completely ignored${$.escape(title)}