fix: apply iconColor to custom SVG icons from data URLs#2591
fix: apply iconColor to custom SVG icons from data URLs#2591davydkov merged 2 commits intolikec4:mainfrom
Conversation
When custom icons are provided as SVG data URLs, they were previously rendered as <img> tags which cannot inherit the CSS color property. This caused the iconColor style property to have no effect on custom icons, while it worked correctly for bundled icons. This fix decodes SVG data URLs and inlines the SVG content directly into the DOM. This allows the CSS color property to affect the currentColor values in the SVG, enabling iconColor to work correctly. For non-SVG data URLs (PNG, etc.) or if decoding fails, the behavior falls back to using <img> tags as before. Fixes likec4#2568
🦋 Changeset detectedLatest commit: c05757e The changes in this PR will be included in the next version bump. This PR includes changesets to release 18 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughIconRenderer now decodes SVG data URLs and inlines the SVG markup when possible to allow CSS Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/diagram/src/context/IconRenderer.tsx`:
- Around line 72-78: The code in IconRenderer that extracts the encoded SVG
using dataUrl.split(',')[1] can truncate SVGs containing literal commas; instead
find the first comma with indexOf(',') and use substring(index+1) to extract the
payload so all subsequent commas are preserved, then pass that full payload to
decodeURIComponent as before; update the branch in the function handling
URL-encoded data URLs to use this indexOf+substring approach and guard for a
missing comma (return null or fallthrough) rather than using split.
- Around line 111-115: The code in IconRenderer (where svgContent is injected
into the icon variable via dangerouslySetInnerHTML) exposes an XSS risk by
inserting raw decoded SVG; sanitize svgContent before assigning it to
dangerouslySetInnerHTML (e.g., use DOMPurify configured for SVG: import
DOMPurify and call DOMPurify.sanitize(svgContent, { SAFE_FOR_SVG: true,
ADD_TAGS/ADD_ATTR if needed }) or equivalent) and only fall back to
dangerouslySetInnerHTML with the sanitized string; update the IconRenderer logic
that sets icon = <span ... dangerouslySetInnerHTML={{ __html: svgContent }} />
to use the sanitized result and add tests/assertions that icons from untrusted
sources are sanitized.
| } else { | ||
| // URL-encoded format: data:image/svg+xml,%3csvg... | ||
| const encodedContent = dataUrl.split(',')[1] | ||
| if (encodedContent) { | ||
| return decodeURIComponent(encodedContent) | ||
| } | ||
| } |
There was a problem hiding this comment.
split(',')[1] truncates SVG if the data URL contains unencoded commas.
If an SVG data URL contains literal commas after the initial delimiter (e.g. data:image/svg+xml,<svg fill="rgb(0,0,0)"...), split(',')[1] returns only the fragment before the second comma. Using indexOf + substring preserves the full content.
Suggested fix
- const encodedContent = dataUrl.split(',')[1]
- if (encodedContent) {
- return decodeURIComponent(encodedContent)
- }
+ const commaIndex = dataUrl.indexOf(',')
+ if (commaIndex !== -1) {
+ const encodedContent = dataUrl.substring(commaIndex + 1)
+ if (encodedContent) {
+ return decodeURIComponent(encodedContent)
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else { | |
| // URL-encoded format: data:image/svg+xml,%3csvg... | |
| const encodedContent = dataUrl.split(',')[1] | |
| if (encodedContent) { | |
| return decodeURIComponent(encodedContent) | |
| } | |
| } | |
| } else { | |
| // URL-encoded format: data:image/svg+xml,%3csvg... | |
| const commaIndex = dataUrl.indexOf(',') | |
| if (commaIndex !== -1) { | |
| const encodedContent = dataUrl.substring(commaIndex + 1) | |
| if (encodedContent) { | |
| return decodeURIComponent(encodedContent) | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@packages/diagram/src/context/IconRenderer.tsx` around lines 72 - 78, The code
in IconRenderer that extracts the encoded SVG using dataUrl.split(',')[1] can
truncate SVGs containing literal commas; instead find the first comma with
indexOf(',') and use substring(index+1) to extract the payload so all subsequent
commas are preserved, then pass that full payload to decodeURIComponent as
before; update the branch in the function handling URL-encoded data URLs to use
this indexOf+substring approach and guard for a missing comma (return null or
fallthrough) rather than using split.
davydkov
left a comment
There was a problem hiding this comment.
Hey @kaigritun!
Thank you very much for the fix!
Do you mind to add changeset?
Summary
Fixes #2568
When custom icons are provided as SVG data URLs, they were previously rendered as
<img>tags which cannot inherit the CSScolorproperty. This caused theiconColorstyle property to have no effect on custom icons, while it worked correctly for bundled icons.Problem
As reported in the issue, custom icons rendered like this:
The
currentColorvalues in the SVG refer to the SVG's internal context, not the parent HTML context. Since<img>tags are opaque to CSS, the--likec4-icon-colorCSS variable has no effect.Solution
This PR decodes SVG data URLs and inlines the SVG content directly into the DOM:
This allows the CSS
colorproperty (set via--likec4-icon-color) to affect thecurrentColorvalues in the SVG.Key changes in
packages/diagram/src/context/IconRenderer.tsx:Added
decodeSvgDataUrl()helper function that:data:image/svg+xml)Updated
IconRenderercomponent to:<img>tag for non-SVG images or failed decodingBehavior:
<img>(unchanged behavior)<img>(unchanged behavior)Testing
Manually verified that:
currentColornow respecticonColorSummary by CodeRabbit
New Features
Bug Fixes