fix(runtime): map camelCase SVG props to kebab-case attributes#6776
fix(runtime): map camelCase SVG props to kebab-case attributes#6776Arul1998 wants to merge 2 commits into
Conversation
SVG attribute matching is case-sensitive, so camelCase props spread onto SVG elements (e.g. strokeWidth) were set as-is and silently ignored by the renderer. Map them to their kebab-case attribute names like React does, leaving SVG's genuinely camelCase attributes (viewBox, preserveAspectRatio, etc.), xml/xmlns prefixed names, and xlink handling unchanged. The mapping is gated behind the svg build conditional. Fixes stenciljs#5674
johnjenkins
left a comment
There was a problem hiding this comment.
❤️
Can you add a spec test to src/runtime/test/svg-element.spec.tsx 🙏
|
Added integration specs to svg-element.spec.tsx one covering the spread-props reproduction from the issue rendered through the vdom, and one covering update/removal of a mapped attribute via a @prop. Thanks for the review! |
|
hey @Arul1998 - again, thanks for looking into this.
Sometimes you just don't think things through until you're forced to think them through. The reasons:
Ultimately, users can achieve a spread on svg children via Again - thank you so much for supporting Stencil with your time 🙏 |
SVG attribute matching is case-sensitive, so camelCase props spread onto SVG elements (e.g. strokeWidth) were set as-is and silently ignored by the renderer. Map them to their kebab-case attribute names like React does, leaving SVG's genuinely camelCase attributes (viewBox, preserveAspectRatio, etc.), xml/xmlns prefixed names, and xlink handling unchanged. The mapping is gated behind the svg build conditional.
Fixes #5674
What is the current behavior?
GitHub Issue Number: #5674
camelCase SVG props are set verbatim as attributes. Since SVG attribute matching is case-sensitive, an attribute named
strokeWidthdoes not matchstroke-widthand the styling is silently ignored — no warning or error. This mainly bites when spreading an object of props onto an SVG element (React-style prop objects, shared prop factories, etc.):renders
<circle ... strokeWidth="3">, which has no visual effect.What is the new behavior?
In an SVG context, camelCase prop names are mapped to their kebab-case SVG attribute names (
strokeWidth→stroke-width,fillOpacity→fill-opacity, …) for setting, updating, and removal — matching React's behavior for SVG props.Deliberately excluded from the mapping:
viewBox,preserveAspectRatio,gradientTransform,baseFrequency, etc.) — passed through unchanged via an explicit exception listtabIndexandcrossOrigin, which map to all-lowercase attribute names rather than kebab-casexml/xmlnsprefixed names — left unmodifiedxlinkhandling — the mapping runs after it, soxlinkHrefcontinues to work as beforeThe mapping only applies in SVG contexts (
isSvg), is gated behind thesvgbuild conditional so it is tree-shaken from applications that don't render SVG, and non-SVG elements are unaffected.The kebab-case prop names Stencil's JSX typings declare today (
stroke-width={3}) continue to work exactly as before — this is purely additive.Documentation
N/A — behavior now matches what the JSX allows; happy to add a docs note to the SVG guide in stenciljs/site if desired.
Does this introduce a breaking change?
Previously, camelCase names in SVG contexts produced attributes that SVG ignores, so no working code depended on the old behavior.
Testing
set-accessor.spec.ts: setting, updating, and removing a camelCase prop as its kebab-case attribute; camelCase-in-spec attributes (viewBox,preserveAspectRatio) preserved;tabIndex→tabindex; and no mapping outside SVG contexts. Fullset-accessorspec: 132/132 passing.<circle cx="15" cy="5" r="3" stroke="green" stroke-width="3">, computed style reportsstroke-width: 3px, and nostrokeWidthattribute is present.Other information
The exception list mirrors the camelCase attribute names from the SVG specification (the same set React preserves). It lives behind the
BUILD.svgconditional and a@__PURE__annotation, so it adds no bytes to builds without SVG rendering.