Skip to content

convertStyleToAttrs should not apply on html foreignObject #2222

Description

@maxired

Describe the bug

When applied to <foreignObject> content, the convertStyleToAttrs plugin can break rendering. Inside <foreignObject>, the children are typically HTML elements (XHTML namespace), not SVG. Converting their inline style declarations into individual attributes is not valid in HTML, so the styling is lost.

Here is an SVG to reproduce:

<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_29872_18151)">
<rect width="200" height="200" fill="#0E60D4"/>
<foreignObject x="45.9956" y="82.2183" width="185.415" height="152.969"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(11.91px);clip-path:url(#bgblur_2_29872_18151_clip_path);height:100%;width:100%"></div></foreignObject>
<g>
<circle opacity="0.9" cx="144" cy="142" r="45.5" fill="#1368CE" stroke="white" stroke-width="5"/>
<path d="M160.759 117.1L170.553 125.114L138.104 164.796L117.283 147.319L125.748 137.913L136.713 146.51L160.759 117.1Z" fill="white"/>
</g>
</g>
<defs>
<clipPath id="bgblur_2_29872_18151_clip_path" transform="translate(-45.9956 -82.2183)"><rect x="68.9121" y="115.873" width="64.1024" height="11.7216" rx="5.86081" transform="rotate(-9.64387 68.9121 115.873)"/>
</clipPath>
</defs>
</svg>

To Reproduce

  1. Save the SVG above to a file.
  2. Optimise it with SVGO using the convertStyleToAttrs plugin (it is part of the default preset).
  3. Open the resulting SVG in a browser.

Expected behavior

The optimised SVG should render the same as the source: the clip-path declared on the inner <div> should still apply.

Screenshots

Image

In the broken output above, the inner <div>'s clip-path no longer applies, so the white circle behind it is no longer masked.

Desktop (please complete the following information):

  • SVGO Version:
  • Node.js Version:
  • OS:

Additional context

The root cause appears to be that the plugin processes every element in the document, including HTML descendants of <foreignObject>. A CSS property such as clip-path is valid inside a style attribute on an HTML element, but is not a valid HTML presentation attribute, so it is silently dropped by the browser when split out. A safer behavior would be to skip any descendant of a <foreignObject> (or any element in a non-SVG namespace).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions