SVG gradient rendered differently in Live Preview vs Preview

Steps to reproduce

  1. Insert the following SVG into a note.
<svg viewBox="0 0 3760 150">
	<title>Timeline 2022</title>
	  <defs>
      	<linearGradient id="Gradient3" x1="0" x2="1" y1="0" y2="0">
        	<stop offset="0%" stop-color="black"/>
        	<stop offset="50%" stop-color="white"/>
        	<stop offset="100%" stop-color="black"/>
      </linearGradient>
	</defs>
	<g class="bars">
		<rect fill="url(#Gradient3)" x="0" y="20" width="3760" height="45"></rect>
	</g>
	<g class="labels" style="font-size:50px;" text-anchor="middle">
		<text fill="#AAAAAA" x="0" y="120" text-anchor="start">January</text>
		<text fill="#AAAAAA" x="320" y="120" text-anchor="start">February</text>
		<text fill="#AAAAAA" x="610" y="120" text-anchor="start">March</text>
		<text fill="#AAAAAA" x="930" y="120" text-anchor="start">April</text>
		<text fill="#AAAAAA" x="1240" y="120" text-anchor="start">May</text>
		<text fill="#AAAAAA" x="1560" y="120" text-anchor="start">June</text>
		<text fill="#AAAAAA" x="1870" y="120" text-anchor="start">July</text>
		<text fill="#AAAAAA" x="2190" y="120" text-anchor="start">August</text>
		<text fill="#AAAAAA" x="2510" y="120" text-anchor="start">September</text>
		<text fill="#AAAAAA" x="2820" y="120" text-anchor="start">October</text>
		<text fill="#AAAAAA" x="3140" y="120" text-anchor="start">November</text>
		<text fill="#AAAAAA" x="3450" y="120" text-anchor="start">December</text>
	</g>
	<g>
		<circle cx="310" cy="44" r="30" stroke="black" stroke-width="7" fill="white" />
	</g>
</svg>
  1. Click elsewhere in the file so that Live Preview can render the SVG.
  2. Switch to Preview (or Read mode) and notice that the gradient is now not displayed.

Expected result

I expect the SVG to be rendered the same in both modes:

Actual result

In read mode, the gradient disappears from the SVG

Environment

  • Operating system: Windows 11
  • Debug info:
    SYSTEM INFO:
    Obsidian version: v0.13.23
    Installer version: v0.12.10
    Operating system: Windows 10 Pro 10.0.22000
    Login status: logged in
    Catalyst license: vip
    Insider build toggle: on
    Live preview: on
    Legacy editor: off
    Base theme: dark
    Community theme: none
    Snippets enabled: 6
    Safe mode: off
    Plugins installed: 45
    Plugins enabled: 0

RECOMMENDATIONS:
Custom theme: for cosmetic issues, please first try updating your theme to latest. If still not fixed, please try to make the issue happen in the help vault or disable community theme and snippets.


Additional information

The debug info says six snippets are enabled; however, no snippets are enabled.

FWIW, I was able to duplicate this in the Obsidian Help vault. Note that the SVG shows up correctly in reading mode using the legacy editor.

works for me in the help vault

Interesting. I just tried again in the Help Vault and the bar is displaying as expected. :smiley:

Thanks for checking @WhiteNoise, and for building this wonderful tool.

Odd! What do you think changed?

I am not sure. I just checked once more and the gradient bar in the SVG is not displaying in reading mode in the help vault again:

image

It does show in the edit mode:

image

If you were not having issues, I would blame this on something I was doing. I’ll try playing around a bit to see if I can determine how it worked one time for me but not the other two times. I mean my issue might still be the person between the chair and keyboard. :wink:

It has always been a problem for me, no matter what I do.

The issue is that in preview mode the element #Gradient3 is defined twice in DOM and the first instance of it is hidden. Once in markdown-source-view and again in markdown-reading-view.

Mermaid charts have similar issues, they also use IDs that get duplicated. Either Obsidian should not render markdown-source-view and markdown-reading-view in DOM at the same time OR they should be encapsulated in shadow DOM.
But I’m not sure what framework Obsidian uses, maybe neither is possible without major changes.

A quick fix for this issue would be if Obsidian changed the IDs in markdown-reading-view to something unique.
For example if I run this in the developer console, it finds all the elements with IDs and then appends -fix to those IDs, but also makes the change in the places where the IDs are used.

document.querySelectorAll(".markdown-reading-view [id]").forEach((el) => {
  const id = el.id;
  const readingView = document.querySelector(".markdown-reading-view");

  readingView.innerHTML = readingView.innerHTML.replaceAll(id, id + "-fix")
})

This makes the gradient visible again and makes arrows visible in mermaid diagrams (Mermaid: arrows not showing).

But I can’t say if this could have any negative side-effects… it feels a bit hacky.
Maybe the replace should be done only to elements which are within an <svg> element, to avoid accidentally changing user written content.

Something like this should be safer, modifies only SVGs:

document.querySelectorAll(".markdown-reading-view svg").forEach((svg) => {
  svg.querySelectorAll("[id]").forEach((el) => {
    const id = el.id;
    const randomInt = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);

    svg.innerHTML = svg.innerHTML.replaceAll(id, `${id}-${randomInt}`);
  })
});

This way it doesn’t touch the user written text, but also, because it doesn’t rewrite the whole reading view HTML, it keeps event references intact outside SVG elements.

And this way you could even use multiple SVGs which use clashing IDs.
At the moment, if you include 2 SVGs and one defines a gradient:

<linearGradient id="gradient" x1="0" x2="1" y1="0" y2="0">
  <stop offset="0%" stop-color="black"/>
  <stop offset="50%" stop-color="white"/>
  <stop offset="100%" stop-color="black"/>
</linearGradient>

And the other SVG:

<linearGradient id="gradient" x1="0" x2="1" y1="0" y2="0">
  <stop offset="0%" stop-color="red"/>
  <stop offset="50%" stop-color="yellow"/>
  <stop offset="100%" stop-color="red"/>
</linearGradient>

Then when the second SVG tries to use #gradient, it will actually refer to the gradient of the first SVG and so the second SVG will render incorrectly.

Maybe it could be a temporary fix until shadow DOM is implemented?

1 Like

I hadn’t thought of that, but it makes perfect sense!