diff --git a/.changeset/shy-brooms-tell.md b/.changeset/shy-brooms-tell.md new file mode 100644 index 000000000000..022423d3a4b2 --- /dev/null +++ b/.changeset/shy-brooms-tell.md @@ -0,0 +1,7 @@ +--- +'astro': patch +--- + +Fix rendering of HTML boolean attributes like `open` and `async`. + +Fix rendering of HTML and SVG enumerated attributes like `contenteditable` and `spellcheck`. diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index 9d9fd076a227..4217330bf047 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -11,6 +11,10 @@ export { createMetadata } from './metadata.js'; export { escapeHTML, unescapeHTML } from './escape.js'; const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i; +const htmlBooleanAttributes = /^(allowfullscreen|async|autofocus|autoplay|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|itemscope)$/i; +const htmlEnumAttributes = /^(contenteditable|draggable|spellcheck|value)$/i; +// Note: SVG is case-sensitive! +const svgEnumAttributes = /^(autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i; // INVESTIGATE: // 2. Less anys when possible and make it well known when they are needed. @@ -327,10 +331,17 @@ const STATIC_DIRECTIVES = new Set(['set:html', 'set:text']); // A helper used to turn expressions into attribute key/value export function addAttribute(value: any, key: string) { - if (value == null || value === false) { + if (value == null) { return ''; } + if (value === false) { + if (htmlEnumAttributes.test(key) || svgEnumAttributes.test(key)) { + return unescapeHTML(` ${key}="false"`); + } + return '' + } + // compiler directives cannot be applied dynamically, log a warning and ignore. if (STATIC_DIRECTIVES.has(key)) { // eslint-disable-next-line no-console @@ -345,8 +356,8 @@ Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the return unescapeHTML(` ${key.slice(0, -5)}="${toAttributeString(serializeListValue(value))}"`); } - // Boolean only needs the key - if (value === true && key.startsWith('data-')) { + // Boolean values only need the key + if (value === true && (key.startsWith('data-') || htmlBooleanAttributes.test(key))) { return unescapeHTML(` ${key}`); } else { return unescapeHTML(` ${key}="${toAttributeString(value)}"`);