This is basically just re-opening issue #856 but without the focus being interop.
transform: scale(2)
is equivalent to transform: scale(2, 2), i.e. scaling both x/y.
scale: 2
is equivalent to transform: scale(2, 1), i.e. scaling x only.
That seems inconsistent and surprising. I've yet to meet anyone who expects that scale: 2 scales in only x.
I understand that scale can be used for 3D transforms, but scale3d() requires three arguments so I don't think there's any parallel between the arguments used there and here.
CC: @ewilligers @FremyCompany @gregwhitworth
So here's the problem. You can apply scale: X to 3d content. Does scale: 2 expand in your proposal to scale: 2 2 1 or scale: 2 2 2?
If the former, that's confusing - on 3d content, scale: 2 will expand your stuff in the xy plane, but the z plane is left alone. This breaks expectations with 2d-authored content.
If the latter, that's confusing - what does scale: 2 3 expand to? It also means you would need to specify all 3 options if you ever want a 2d scale, to make sure the z is set to 1.
We went with the third confusing option: it expands to 2 1 1, so only the axises you specify expand at all. This breaks expectations with scale(2), tho.
Since all the options were bad, we went with the one that's bad in a consistent way, at least.
scale: 2 2 1 seems best to me. I expect > 95% of the time people will be using it with 2D content -- and scale most naturally corresponds to scale(), not scale3d(). Once you're working with 3D content you already expect to be specifying three numbers.
I guess what I'm saying is, make scale consistent with scale(), not scale3d().
Would it be possible to have scale: 3 expand to scale: 3 3 3? If the current z-axis position (and z-axis transform origin) is 0, scaling it has no effect. _Except_ if it then upgrades to "3D transform" mode and causes complications that way.
If that's the case (complications from switching to 3D mode), the default should be as @birtles proposed, and have a single value behave the same as the scale() function.
Name: scale
Value: none | \{1,3} The
scaleproperty accepts 1-3 values, each specifying a scale along one axis, in order X, Y, then Z. Unspecified scales default to1.
In general, there are two consistent expansions of a single value for a property: either repeat it for all possible values (_a → a a a_) or set one to the explicit value and reset the rest to the default value (_a → a 1 1_). The more useful and, preferably, intuitive one of those choices should be specified, but inconsistent expansions are not unheard of if they are justified. In this case, resetting unspecified values is neither useful nor intuitively expected, while repeating them is useful and intuitively expected for only one of two scenarios, but that is still more than none. The inconsistent expansion _a → a a 1_ leads also to useful and expected results in only one of two scenarios, but it's the more common one.
| code | syntax | 2D context | 3D context |
| ---- | ---- | ---- | ---- |
| scale: 2 = scale: 2 2 2 | consistent | unintuitive + harmful | intuitive + useful |
| scale: 2 = scale: 2 1 1 | consistent | unintuitive + harmless | unintuitive + harmless |
| scale: 2 = scale: 2 2 1 | inconsistent | intuitive + useful = compatible | unintuitive + harmful |
That means, although all three choices are bad, they are not equally bad. Repetition is the least bad. However, we need also to consider the case where two out of three values have been set explicitly. There are (at least) four possible expansions for the third value: reset to default (_a b → a b 1_), repeat the first (_a b → a b a_) or the last (_a b → a b b_) value and set it to a calculated value(_a b → a b c_). Resetting is intuitively compatible with the less-favored option for the single-value case, whereas both repetition variants are consistent but arbitrary and hence not intuitive, and the derivation is neither consistent nor (generally) intuitive.
| code | syntax | 2D context | 3D context |
| ---- | ---- | ---- | ---- |
| scale: 2 3 = scale: 2 3 1 | consistent + systematic | intuitive + useful = compatible | unintuitive + ambivalent |
| scale: 2 3 = scale: 2 3 2 | consistent + arbitrary | unintuitive + harmful | unintuitive + ambivalent |
| scale: 2 3 = scale: 2 3 3 | consistent + arbitrary | unintuitive + harmful | unintuitive + ambivalent |
| scale: 2 3 = scale: 2 3 2.5 | inconsistent + arbitrary | unintuitive + harmful | unintuitive + ambivalent |
That means, a choice needs to be made whether the expansions of a single value and of two values into three values should be consistent with each other or if there is enough reason to treat them differently. I believe there is. The most useful and still intuitive and somewhat consistent solution is to repeat a single value thrice, but to set the third value to 1 if only two values are provided.
Value: \<'scale-x'> \<'scale-y'> \<'scale-z'>? | \
The
scaleproperty accepts 1-3 values, each specifying a scale along one axis, in order X, Y, then Z.
If only the last, Z value is left unspecified, it defaults to1.
If only a single value is specified, all axis scale values are set to this same value.
| code | draft | proposed |
| ---- | ---- | ---- |
| scale: 2 | scale: 2 1 1 | scale: 2 2 2 |
| scale: 2 3 | scale: 2 3 1 | scale: 2 3 1 |
PS: In other words, you can only have scale: 2 equal scale: 2 2 (or transform: scale(2)) if this also equals scale: 2 2 2 (or transform: scale3d(2)).
Why you want to add "scale"? the transform:scale(2) is sufficient. Too many of unnecessary properties. Too low performance because of too many properties to analyze by browser...
I also kind of fail to see the point of the separate properties, fwiw. I get that you may want to cascade them separately, I guess, but it makes transform inconsistent overall. For example, transform: none wouldn't be enough for an element to be completely untransformed.... Maybe it's worth another issue though, to keep this on topic.
Author intent would be clearer if we required at least two scales with none | <number>{2,3}
translate could optionally be similar: none | <length-percentage>{2} <length>?
But in transform style is there order of values is important for example:
transform: scale(-2) translate(50px, 50px);
and
transform: translate(50px, 50px) scale(-2);
do you know how big perfornmance impact has "more CSS styles"? Test Mozilla Firefox current version and Mozilla Firefox 3. scale as separate style is not needed.
And living demo: order of transforms (scale and translate and rotate) is important.
https://jsfiddle.net/p7sb1o6h/
If described as:
html div
{
scale:-2;
translate:50px 50px;
}
html body div
{
scale:-2;
}
the order is unknown.
Would it be possible to have scale: 3 expand to scale: 3 3 3? If the current z-axis position (and z-axis transform origin) is 0, scaling it has no effect. Except if it then upgrades to "3D transform" mode and causes complications that way.
I spoke to @mattwoodrow about this:
Our current behaviour is to force an active layer if the resulting transform contains any sort of 3d components, so expanding it to 'scale: 2 2 2' would be a regression for us. It's fairly easy to change that, but harder to know if any web content is depending on the behaviour.
If I recall correctly blink does something similar, but instead uses the presence of 3d transform function (scale3d, transform3d etc), regardless of what the values passed are. They could probably go either way, depending on how they interpret 'scale: 2 2 2'.
So it sounds like it would be some work to make scale: 2 expand to scale: 2 2 2 and I wonder if it is worthwhile (I would be glad to be told otherwise though!). Matt also asked if it makes sense to introduce a separate scale3d property.
If that's the case (complications from switching to 3D mode), the default should be as @birtles proposed, and have a single value behave the same as the scale() function.
I (unsurprisingly) still think this is the preferred behavior here.
CC: @mstange
@Nadya678 The order of functions inside the transform property is relevant, but the translate, scale and rotate properties cascade normally by specifity. That means they are only equivalent to the equally functions in trivial cases. Both approaches are useful in different scenarios.
@birtles Right, I'm pretty sure that having it expand to 2 2 2 is the worst of the three options. It triggers spurious 3d-ness, as you note, and it also doesn't give a clear answer for what to do with scale: 2 3.
The other two options are still both bad, and I don't have a very strong opinion on which one to use, but my reasoning for/against each is already stated above. If you'd like to litigate this in the group, feel free - I can live with either option.
Thanks Tab. Yes, it sounds like the next step is to bring it before the group. I'll be in Toronto next week so I should be able to make the telcon then.
FWIW, WebKit also uses 3D-style transform functions (e.g. scale3D(), or what would be a three-argument scale:) as a trigger to do GPU compositing.
@smfr Not just WebKit. And the spec makes other distinctions for 3D-transformed elements versus elements with only 2D transformations. So it's probably important that all the individual transform properties clearly distinguish between 2D and 3D forms.
The Working Group just discussed [css-transforms-2] `scale` property behavior differs from `scale()` function, and agreed to the following resolutions:
RESOLVED: In the 2d case a single value is in both x and y and in 3d the z is always 1 (Option b)The full IRC log of that discussion
<dael> Topic: [css-transforms-2] scale property behavior differs from scale() function
<dael> github: https://github.com/w3c/csswg-drafts/issues/2109
<dael> birtles: The sumamry is that the scale property takes 1 2 or 3 numbers. Q is what it should do when you spec 1 number. We have 4 possibilities.
<TabAtkins> scale:2, what does it expand to?
<TabAtkins> (a) scale: 2 1 1;
<dael> birtles: One is the remaining numbers become 1. That's current spec. Very simple from syntax and consitant if doing 2d or 3d.
<dael> birtles: Disadvantage is it's inconsistant with scale from transform prop where if you only do 1 number scales in x or y and it's quite unintuitive.
<TabAtkins> (b) scale: 2 2 1
<dael> birtles: 2nd is to make the second value match the first and last value be 1 so you don't scale in z. More complex for syntax and inconsistant between 2d and 3d.
<dael> birtles: But I think it's intuitive in 2d case which is most common.
<TabAtkins> (c) scale: 2 2 2;
<dael> birtles: 3rd which we dismissed is make all numebrs the same so scale(2) is scaling in x, y, and z
<dael> birtles: 4th is to make scale property only take 2 values and possible introduce a scale 3d property.
<TabAtkins> (d) scale: 2 2, but add scale-3d:2 that expands into scale-3d:2 2 2;
<dael> birtles: I lean options 2 but 4 might be interesting.
<dael> Rossen_: Can you expand on why #3 was ruled out? What was it introducing in 3d. If you set scale in a single value that's in all 3 dimensions
<dael> birtles: In most impl as soon as there's a 3d component it makes it an active layer. Changable, but non-trivial.
<dael> TabAtkins: Other 2 transform properties follow principle that if you spec less you get 2d and more you get 3d. Would be confusing if scale worked differently
<dael> dbaron: What about option of disallowing a single but allowing 2 or 3 values and determining 2d and 3d based on number of values
<dael> TabAtkins: Reasonable option.
<dael> birtles: Reasonable but a little less then intuitive but also not consistant with scale function so authors may assume it will work.
<dael> fremy: When we started impl in edge I found it confusing that when you spec 1value it only scales in 1 direction. Seems reasonable to scale in 2 directions when spec one value. I'm not sure why we care about 3d for these individual transforms. i'm at a loss why we'd do 3d for these. Anyone doing 3d will use transform not scale.
<dael> fremy: I'm in favor of matching scale.
<dael> TabAtkins: I strongly disagree where order is very important in 2d as well as 3d. So you have to think about order no matter what. 3d version is no less..the order is no less max intuitive for 3d. It's still roughly the thing you want to do if you're using them sep.
<dael> TabAtkins: It makes jsut as much sense in 2d or 3d and order matters jsut as much so no reason to shut off 3d.
<dael> eric: I'm in favor of birtles suggestion
<dael> nainar: I agree with TabAtkins that we shouldn't turn off 3d. Anyone working in 3d is working in all 3 values and if you want to scale in 3 directions you have to say so explicitly. Doens't seem like a painpoint. Default should be simplier 2d where scale 2 means scale in both 2d directions
<alex_antennahouse> +1 (b)
<nainar> s/nainar/??
<dael> ??: Can we reasolve on b? anyone not like b?
<smfr> s/??/smfr/
<dael> Rossen_: B is in the 2d case a single value is in both x and y and in 3d the z is always y. correct?
<dael> smfr: Yes.
<dael> Rossen_: Any objections or opinions again?
<AmeliaBR> s/is always y/is always 1/
<TabAtkins> (previous nainar was ameliabr, but I'm not sure if a regex will hit it now; I forget whether stacked regexes work)
<dael> RESOLVED: In the 2d case a single value is in both x and y and in 3d the z is always 1 (Option b)
Fixed in 48f19e8
Most helpful comment
I guess what I'm saying is, make
scaleconsistent withscale(), notscale3d().