1

The goal is to create a cartesian space that correctly displays objects and text with an inverted vertical coordinate system, so that text is not displayed upsidedown. I would like to not have to embed the text inside of a parent <g> element.

This snippet works:

<style>
svg.cartesian { transform: scaleY(-1); }
svg.cartesian text { transform: scaleY(-1); }
</style>
<svg class="cartesian" viewBox="-100 -100 200 200" preserveAspectRatio="xMidYMid meet">
<path d="M0 -100 V 200" stroke="green" stroke-width="0.5" stroke-opacity="0.5" />
<path d="M-100 0 H 200" stroke="green" stroke-width="0.5" stroke-opacity="0.5" />
<circle cx=20 cy=20 r=1 />
<g transform="translate(20, 20)">
<text>(20, 20)</text>
</g>
</svg>

However, if the translate() command is moved inside the <text> element, it does not work; the text does not get translated to the new position:

<style>
svg.cartesian { transform: scaleY(-1); }
svg.cartesian text { transform: scaleY(-1); }
</style>
<svg class="cartesian" viewBox="-100 -100 200 200" preserveAspectRatio="xMidYMid meet">
<path d="M0 -100 V 200" stroke="green" stroke-width="0.5" stroke-opacity="0.5" />
<path d="M-100 0 H 200" stroke="green" stroke-width="0.5" stroke-opacity="0.5" />
<circle cx=20 cy=20 r=1 />
<text transform="translate(20, 20)">(20, 20)</text>
</svg>

Why?

Rick
  • 43,029
  • 15
  • 76
  • 119
  • @RobertLongson I'm a little confused: there's only one svg transform in both cases. You mean the application order of the CSS transform and the svg transform changes between each case...? – Rick Jun 23 '19 at 16:43
  • @RobertLongson Ok I see. However the translate command does not appear to happen at all in the second case. Is it cancelled by the CSS transform...? – Rick Jun 23 '19 at 18:00
  • @RobertLongson When you say "it does happen", are you saying that- in addition to both of the transforms being applied and the CSS being applied last- that the second (CSS) transform is applied from the *original* position, as if the first (svg translate) transform had never occurred? Because it is clear no translation has been applied to the final image. This is what I meant by "cancelled by the CSS transform". – Rick Jun 23 '19 at 18:45
  • I suggest you experiment for yourself and find out. – Robert Longson Jun 23 '19 at 19:07
  • @RobertLongson I have, and that is what appears to be happening. I'm looking for confirmation that my interpretation of what is happening is correct, especially since that interpretation seems like it might be at odds with your explanation of what is happening (though I'm not sure) and I assume you are probably more knowledgeable than me (I'm not much of an html/css person). – Rick Jun 23 '19 at 19:14
  • 1
    @RobertLongson while I agree the order is important, the issue here is a different thing and is a simple override of the transform by CSS. – Temani Afif Jun 23 '19 at 19:33

1 Answers1

2

The answer is easier than what you expect. In the second case you are overriding1 the translate with the scale that's why it's not working:

enter image description here

If you want 2 transformation into the same element, you need to put them in the same transform:

<style>
svg.cartesian { transform: scaleY(-1); }
</style>
<svg class="cartesian" viewBox="-100 -100 200 200" preserveAspectRatio="xMidYMid meet">
<path d="M0 -100 V 200" stroke="green" stroke-width="0.5" stroke-opacity="0.5" />
<path d="M-100 0 H 200" stroke="green" stroke-width="0.5" stroke-opacity="0.5" />
<circle cx=20 cy=20 r=1 />
<text transform="translate(20, 20) scale(1,-1)">(20, 20)</text>
<text transform="scale(1,-1) translate(20, 20)">(20, 20)</text>
</svg>

As you can see I added both cases to demonstrate that the order is important.

Related: Why does order of transforms matter? SVG rotate/scale doesn't give the same result as scale/rotate


1 From the specification:

All document language-based styling must be translated to corresponding CSS rules and either enter the cascade at the user agent level or be treated as author level rules with a specificity of zero placed at the start of the author style sheet. A document language may define whether a presentational hint enters at the UA or author level of the cascade; if so, the UA must behave accordingly. For example, [SVG11] maps its presentation attributes into the author level

Then

Each style rule has a cascade origin, which determines where it enters the cascade. CSS defines three core origins:

Author Origin

The author specifies style sheets for a source document according to the conventions of the document language.

User Origin

The user may be able to specify style information for a particular document. For example, the user may specify a file that contains a style sheet or the user agent may provide an interface that generates a user style sheet (or behaves as if it did).

User Agent Origin

Conforming user agents must apply a default style sheet (or behave as if they did). A user agent’s default style sheet should present the elements of the document language in ways that satisfy general presentation expectations for the document language

Then

The cascade takes an unordered list of declared values for a given property on a given element, sorts them by their declaration’s precedence as determined below, and outputs a single cascaded value.

And you will find the full list of rules and you will understand why the CSS is overriding the attribute one. You will so see that at the end only one rule should be selected.

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • I was beginning to think that this was the answer. Is that override because it is two different kinds of incompatible transforms (a CSS transform and a SVG transform)? If so is that documented somewhere? – Rick Jun 23 '19 at 19:29
  • @RickTeachey it's not about *incompatibility* but how CSS works. Basically CSS will win the game against attribute. It's like you define height of image using atttribute then later use CSS to define the height. I will find the relevant Specification for this – Temani Afif Jun 23 '19 at 19:31
  • That make sense I suppose... It's still odd so me that a scale transform "cancels out" a translate transform. – Rick Jun 23 '19 at 19:35
  • 1
    @RickTeachey no, it's not scale that cancel translate .. but a CSS rule that cancel the attribute one. It doesn't matter what contain the transform. The CSS will be the one that will get applied which is logical because you cannot apply the same thing more than once. It's like you have many CSS rules that apply to the same element: the most specific will win the game – Temani Afif Jun 23 '19 at 19:39
  • I understand. It is still, though, for a novice, pretty surprising. Thanks for the help! – Rick Jun 23 '19 at 19:42
  • @RickTeachey I added the relevant part of the specification that describe this. It's somehow complex at the beginning but you will understand how all this works – Temani Afif Jun 23 '19 at 19:57