3

I'm trying to build an SVG using transform attribute. But while my SVG looks as expected in Chrome and Firefox it looks broken in Safari. It looks like Safari doesn't respect transform-origin attribute and always applies the transform like transform-origin has "0 0" value.

I need to animate transform attribute and I need the resulting SVG to look the same in all browsers. I tried to work around the issue by providing different values to transform-box CSS property, but without success.

Is there any workaround for the issue?

Below is an example illustrating the issue. All images should look the same. They look the same in Chrome and Firefox, but not in Safari.

h1 {
  font-family: sans-serif;
}

figure {
  border: thin #c0c0c0 solid;
  display: inline-flex;
  flex-flow: column;
  padding: 5px;
  max-width: 200px;
  margin: auto;
}

figcaption {
  margin-top: 5px;
  background-color: #222;
  color: #fff;
  font: smaller sans-serif;
  padding: 3px;
  text-align: center;
}
<h1>1. Reference image</h1>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <circle cx="100" cy="100" r="100" stroke="none" fill="black"/>
    <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
    <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
    
    <circle cx="100" cy="100" r="75" stroke="none" fill="blue"/>
    <line x1="100" y1="25" x2="100" y2="175" stroke="rebeccapurple" stroke-width="1.5"/>
    <line x1="25" y1="100" x2="175" y2="100" stroke="rebeccapurple" stroke-width="1.5"/>
    
    <circle cx="100" cy="100" r="50" stroke="none" fill="red"/>
    <line x1="100" y1="50" x2="100" y2="150" stroke="rebeccapurple" stroke-width="1"/>
    <line x1="50" y1="100" x2="150" y2="100" stroke="rebeccapurple" stroke-width="1"/>
  
    <circle cx="100" cy="100" r="25" stroke="none" fill="yellow"/>
    <line x1="100" y1="75" x2="100" y2="125" stroke="rebeccapurple" stroke-width="0.5"/>
    <line x1="75" y1="100" x2="125" y2="100" stroke="rebeccapurple" stroke-width="0.5"/>
  </svg>
  <figcaption>Figure 1. Reference image, <code>transform</code> is not used. All other images should look the same.</figcaption>
</figure>

<h1>2. <code>transform</code> applied to <code>&lt;g&gt;</code> element</h1>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <g id="target-g-1">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </g>
    </defs>
    
    <use href="#target-g-1" fill="black"/>
    <use href="#target-g-1" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>
    
    <svg x="0" y="0" width="200" height="200" viewBox="0 0 200 200">
      <use href="#target-g-1" fill="red" transform="scale(0.5 0.5)" transform-origin="100 100"/>
      <use href="#target-g-1" fill="yellow" transform="scale(0.25 0.25)" transform-origin="100 100"/>
    </svg>
  </svg>

  <figcaption>Figure 2-1. Nested <code>&lt;svg&gt;</code> has the same size as the outermost <code>&lt;svg&gt;</code>.</figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <g id="target-g-2">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </g>
    </defs>
    
    <use href="#target-g-2" fill="black"/>
    <use href="#target-g-2" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>
    
    <svg x="0" y="50" width="200" height="100" viewBox="0 0 200 100">
      <use href="#target-g-2" fill="red" transform="scale(0.5 0.5)" transform-origin="100 0"/>
      <use href="#target-g-2" fill="yellow" transform="scale(0.25 0.25)" transform-origin="100 33.3333"/>
    </svg>
  </svg>
  <figcaption>Figure 2-2. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along single axis.</figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <g id="target-g-3">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </g>
    </defs>
    
    <use href="#target-g-3" fill="black"/>
    <use href="#target-g-3" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>
    
    <svg x="50" y="50" width="100" height="100" viewBox="0 0 100 100">
      <use href="#target-g-3" fill="red" transform="scale(0.5 0.5)" transform-origin="0 0"/>
      <use href="#target-g-3" fill="yellow" transform="scale(0.25 0.25)" transform-origin="33.3333 33.3333"/>
    </svg>
  </svg>
  <figcaption>Figure 2-3. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along both axes.</figcaption>
</figure>

<h1>3. <code>transform</code> applied to <code>&lt;svg&gt;</code> element</h1>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-1" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-1" fill="black"/>
    <use href="#target-svg-1" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>
    
    <svg x="0" y="0" width="200" height="200" viewBox="0 0 200 200">
      <use href="#target-svg-1" x="0" y="0" fill="red" transform="scale(0.5 0.5)" transform-origin="100 100"/>
      <use href="#target-svg-1" x="0" y="0" fill="yellow" transform="scale(0.25 0.25)" transform-origin="100 100"/>
    </svg>
  </svg>
  <figcaption>Figure 3-1. Nested <code>&lt;svg&gt;</code> has the same size as the outermost <code>&lt;svg&gt;</code>.</figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-2a" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-2a" x="0" y="0" fill="black"/>
    <use href="#target-svg-2a" x="0" y="0" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>
    
    <svg x="0" y="50" width="200" height="100" viewBox="0 0 200 100">
      <use href="#target-svg-2a" x="0" y="-50" fill="red" transform="scale(0.5 0.5)" transform-origin="100 50"/>
      <use href="#target-svg-2a" x="0" y="-50" fill="yellow" transform="scale(0.25 0.25)" transform-origin="100 50"/>
    </svg>
  </svg>
  <figcaption>
    Figure 3-2a. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along single axis.
    Transformed <code>&lt;svg&gt;</code> is shifted.
  </figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-2b" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-2b" x="0" y="0" fill="black"/>
    <use href="#target-svg-2b" x="0" y="0" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>
    
    <svg x="0" y="50" width="200" height="100" viewBox="0 0 200 100">
      <use href="#target-svg-2b" x="0" y="0" fill="red" transform="scale(0.5 0.5)" transform-origin="100 0"/>
      <use href="#target-svg-2b" x="0" y="0" fill="yellow" transform="scale(0.25 0.25)" transform-origin="100 33.333333"/>
    </svg>
  </svg>
  <figcaption>
    Figure 3-2b. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along single axis.
    <code>transform-origin</code> is shifted.
  </figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-3a" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-3a" fill="black"/>
    <use href="#target-svg-3a" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>
    
    <svg x="50" y="50" width="100" height="100" viewBox="0 0 100 100">
      <use href="#target-svg-3a" x="-50" y="-50" fill="red" transform="scale(0.5 0.5)" transform-origin="50 50"/>
      <use href="#target-svg-3a" x="-50" y="-50" fill="yellow" transform="scale(0.25 0.25)" transform-origin="50 50"/>
    </svg>
  </svg>
  <figcaption>
    Figure 3-3a. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along both axes.
    Transformed <code>&lt;svg&gt;</code> is shifted.
  </figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-3b" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-3b" fill="black"/>
    <use href="#target-svg-3b" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>
    
    <svg x="50" y="50" width="100" height="100" viewBox="0 0 100 100">
      <use href="#target-svg-3b" x="0" y="0" fill="red" transform="scale(0.5 0.5)" transform-origin="0 0"/>
      <use href="#target-svg-3b" x="0" y="0" fill="yellow" transform="scale(0.25 0.25)" transform-origin="33.333333 33.333333"/>
    </svg>
  </svg>
  <figcaption>
    Figure 3-3b. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along both axes.
    <code>transform-origin</code> is shifted.
  </figcaption>
</figure>

7
  • This sounds like a bug in WebKit that should be reported bugs.webkit.org — because Safari does claim to support transform-origin.
    – sideshowbarker
    Commented Apr 13, 2021 at 2:48
  • It is supported... in Technology Preview. So no need for a bug-report.
    – Kaiido
    Commented Apr 21, 2021 at 7:48
  • @Kaiido I have just tested it in Safari Technology Preview Release 123 (Safari 14.2, WebKit 15612.1.7.10) and it doesn't work as it should. Commented Apr 21, 2021 at 11:56
  • 1
    @sideshowbarker I found out that there is an existing Safari bug bugs.webkit.org/show_bug.cgi?id=201854 opened in Oct 2019. Have to be mentioned on caniuse.com Commented Apr 21, 2021 at 12:00
  • Up to date TP is 16612.1.7.10
    – Kaiido
    Commented Apr 21, 2021 at 13:30

2 Answers 2

8

Safari has implemented very little of SVG 2 - so the safe way is to only use SVG 1.1 capabilities for cross-browser (there is no transform-origin in SVG 1.1).

The cross-browser way to do this in SVG 1.1. is to transform/translate to the origin, do the scale and then reverse the translation. Something like:

transform="translate(100 200) scale(0.5 0.5) translate(-100 -200)"

Using this technique, the snippet from the question would transform into:

h1 {
  font-family: sans-serif;
}

figure {
  border: thin #c0c0c0 solid;
  display: inline-flex;
  flex-flow: column;
  padding: 5px;
  max-width: 200px;
  margin: auto;
}

figcaption {
  margin-top: 5px;
  background-color: #222;
  color: #fff;
  font: smaller sans-serif;
  padding: 3px;
  text-align: center;
}
<h1>1. Reference image</h1>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <circle cx="100" cy="100" r="100" stroke="none" fill="black"/>
    <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
    <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
    
    <circle cx="100" cy="100" r="75" stroke="none" fill="blue"/>
    <line x1="100" y1="25" x2="100" y2="175" stroke="rebeccapurple" stroke-width="1.5"/>
    <line x1="25" y1="100" x2="175" y2="100" stroke="rebeccapurple" stroke-width="1.5"/>
    
    <circle cx="100" cy="100" r="50" stroke="none" fill="red"/>
    <line x1="100" y1="50" x2="100" y2="150" stroke="rebeccapurple" stroke-width="1"/>
    <line x1="50" y1="100" x2="150" y2="100" stroke="rebeccapurple" stroke-width="1"/>
  
    <circle cx="100" cy="100" r="25" stroke="none" fill="yellow"/>
    <line x1="100" y1="75" x2="100" y2="125" stroke="rebeccapurple" stroke-width="0.5"/>
    <line x1="75" y1="100" x2="125" y2="100" stroke="rebeccapurple" stroke-width="0.5"/>
  </svg>
  <figcaption>Figure 1. Reference image, <code>transform</code> is not used. All other images should look the same.</figcaption>
</figure>

<h1>2. <code>transform</code> applied to <code>&lt;g&gt;</code> element</h1>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <g id="target-g-1">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </g>
    </defs>
    
    <use href="#target-g-1" fill="black"/>
    <use href="#target-g-1" fill="blue" transform="translate(100 100) scale(0.75 0.75) translate(-100 -100)"/>
    
    <svg x="0" y="0" width="200" height="200" viewBox="0 0 200 200">
      <use href="#target-g-1" fill="red" transform="translate(100 100) scale(0.5 0.5) translate(-100 -100)"/>
      <use href="#target-g-1" fill="yellow" transform="translate(100 100) scale(0.25 0.25) translate(-100 -100)"/>
    </svg>
  </svg>

  <figcaption>Figure 2-1. Nested <code>&lt;svg&gt;</code> has the same size as the outermost <code>&lt;svg&gt;</code>.</figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <g id="target-g-2">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </g>
    </defs>
    
    <use href="#target-g-2" fill="black"/>
    <use href="#target-g-2" fill="blue" transform="translate(100 100) scale(0.75 0.75) translate(-100 -100)"/>
    
    <svg x="0" y="50" width="200" height="100" viewBox="0 0 200 100">
      <use href="#target-g-2" fill="red" transform="translate(100 0) scale(0.5 0.5) translate(-100 -0)"/>
      <use href="#target-g-2" fill="yellow" transform="translate(100 33.3333) scale(0.25 0.25) translate(-100 -33.3333)"/>
    </svg>
  </svg>
  <figcaption>Figure 2-2. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along single axis.</figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <g id="target-g-3">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </g>
    </defs>
    
    <use href="#target-g-3" fill="black"/>
    <use href="#target-g-3" fill="blue" transform="translate(100 100) scale(0.75 0.75) translate(-100 -100)"/>
    
    <svg x="50" y="50" width="100" height="100" viewBox="0 0 100 100">
      <use href="#target-g-3" fill="red" transform="translate(0 0) scale(0.5 0.5) translate(-0 -0)"/>
      <use href="#target-g-3" fill="yellow" transform="translate(33.3333 33.3333) scale(0.25 0.25) translate(-33.3333 -33.3333)"/>
    </svg>
  </svg>
  <figcaption>Figure 2-3. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along both axes.</figcaption>
</figure>

<h1>3. <code>transform</code> applied to <code>&lt;svg&gt;</code> element</h1>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-1" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-1" fill="black"/>
    <use href="#target-svg-1" fill="blue" transform="translate(100 100) scale(0.75 0.75) translate(-100 -100)"/>
    
    <svg x="0" y="0" width="200" height="200" viewBox="0 0 200 200">
      <use href="#target-svg-1" x="0" y="0" fill="red" transform="translate(100 100) scale(0.5 0.5) translate(-100 -100)"/>
      <use href="#target-svg-1" x="0" y="0" fill="yellow" transform="translate(100 100) scale(0.25 0.25) translate(-100 -100)"/>
    </svg>
  </svg>
  <figcaption>Figure 3-1. Nested <code>&lt;svg&gt;</code> has the same size as the outermost <code>&lt;svg&gt;</code>.</figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-2a" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-2a" x="0" y="0" fill="black"/>
    <use href="#target-svg-2a" x="0" y="0" fill="blue" transform="translate(100 100) scale(0.75 0.75) translate(-100 -100)"/>
    
    <svg x="0" y="50" width="200" height="100" viewBox="0 0 200 100">
      <use href="#target-svg-2a" x="0" y="-50" fill="red" transform="translate(100 50) scale(0.5 0.5) translate(-100 -50)"/>
      <use href="#target-svg-2a" x="0" y="-50" fill="yellow" transform="translate(100 50) scale(0.25 0.25) translate(-100 -50)"/>
    </svg>
  </svg>
  <figcaption>
    Figure 3-2a. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along single axis.
    Transformed <code>&lt;svg&gt;</code> is shifted.
  </figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-2b" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-2b" x="0" y="0" fill="black"/>
    <use href="#target-svg-2b" x="0" y="0" fill="blue" transform="translate(100 100) scale(0.75 0.75) translate(-100 -100)"/>
    
    <svg x="0" y="50" width="200" height="100" viewBox="0 0 200 100">
      <use href="#target-svg-2b" x="0" y="0" fill="red" transform="translate(100 0) scale(0.5 0.5) translate(-100 0)"/>
      <use href="#target-svg-2b" x="0" y="0" fill="yellow" transform="translate(100 33.33333) scale(0.25 0.25) translate(-100 -33.33333)"/>
    </svg>
  </svg>
  <figcaption>
    Figure 3-2b. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along single axis.
    <code>transform-origin</code> is shifted.
  </figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-3a" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-3a" fill="black"/>
    <use href="#target-svg-3a" fill="blue" transform="translate(100 100) scale(0.75 0.75) translate(-100 -100)"/>
    
    <svg x="50" y="50" width="100" height="100" viewBox="0 0 100 100">
      <use href="#target-svg-3a" x="-50" y="-50" fill="red" transform="translate(50 50) scale(0.5 0.5) translate(-50 -50)"/>
      <use href="#target-svg-3a" x="-50" y="-50" fill="yellow" transform="translate(50 50) scale(0.25 0.25) translate(-50 -50)"/>
    </svg>
  </svg>
  <figcaption>
    Figure 3-3a. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along both axes.
    Transformed <code>&lt;svg&gt;</code> is shifted.
  </figcaption>
</figure>

<figure>
  <svg width="200" height="200" viewBox="0 0 200 200">
    <defs>
      <svg id="target-svg-3b" width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="none">
        <circle cx="100" cy="100" r="100" stroke="none"/>
        <line x1="100" y1="0" x2="100" y2="200" stroke="rebeccapurple" stroke-width="2"/>
        <line x1="0" y1="100" x2="200" y2="100" stroke="rebeccapurple" stroke-width="2"/>
      </svg>
    </defs>
    
    <use href="#target-svg-3b" fill="black"/>
    <use href="#target-svg-3b" fill="blue" transform="translate(100 100) scale(0.75 0.75) translate(-100 -100)"/>
    
    <svg x="50" y="50" width="100" height="100" viewBox="0 0 100 100">
      <use href="#target-svg-3b" x="0" y="0" fill="red" transform="scale(0.5 0.5)"/>
      <use href="#target-svg-3b" x="0" y="0" fill="yellow" transform="translate(33.333333 33.333333) scale(0.25 0.25) translate(-33.333333 -33.333333)"/>
    </svg>
  </svg>
  <figcaption>
    Figure 3-3b. Nested <code>&lt;svg&gt;</code> is centered in the outermost <code>&lt;svg&gt;</code> along both axes.
    <code>transform-origin</code> is shifted.
  </figcaption>
</figure>

1
  • Great answer, absolute life saver! A small note: if instead of SVG attributes you would like to use CSS, you should account for the default transform-origin: 50% 50% in your calculations, i.e. your opening would be: translate(calc(originX% - 50%), calc(originY% - 50%)) ... using the transform-origin="100 200" example with a 200x200 viewbox, the equivalent CSS transform would be transform: translate(0, 50%) scale(0.5) translate(0, -50%)
    – som
    Commented Jun 7, 2022 at 12:46
0

According to https://developer.mozilla.org/de/docs/Web/CSS/transform-origin the problem here is that the transform-origin attribute or the transform-origin CSS style in Safari is only applied to the transform style, not the attribute.

So if you change:

<use href="#target-g-1" fill="blue" transform="scale(0.75 0.75)" transform-origin="100 100"/>

to look like this:

<use href="#target-g-1" fill="blue" style="transform: scale(0.75, 0.75);" transform-origin="100 100"/>

or even this:

<use href="#target-g-1" fill="blue" style="transform: scale(0.75, 0.75);transform-origin:100px 100px"/>

it would work in all browsers.

Not the answer you're looking for? Browse other questions tagged or ask your own question.