Why are "transform" properties grouped? - css

It seems a bit counter intuitive to have properties, which on their own are key:value pairs, be grouped together. Especially since most of them are quite different and can still be used simultaneously as long as you know how to write it. In case it's not clear what I'm talking about, my question is this. Why is the following:
transform: rotate(40deg) scaleX(1,5) translate(-10px, 20px);
Not written like so:
rotation: 40deg;
scaleX: 1.5;
translate: -10px 20px;
This way each property can be manipulated on their own, without having to keep track of the sibling values. There must be a good reason the W3 choose this approach, so does anyone know it?

That's because transforms are not commutative. Therefore, the order matters.
For example, if you use a translation after a rotation, the translation direction will be rotated too.
.first::after {
transform: rotate(180deg) translateX(50px);
}
.second::after {
transform: translateX(50px) rotate(180deg);
}
body {
display: flex;
flex-direction: space-around;
}
div {
height: 100px;
width: 100px;
border: 5px solid;
margin: 25px auto;
}
div::after {
content: 'Hello';
display: block;
height: 100%;
width: 100%;
background: yellow;
opacity: .5;
}
.first::after {
transform: rotate(180deg) translateX(50px);
}
.second::after {
transform: translateX(50px) rotate(180deg);
}
<div class="first"></div>
<div class="second"></div>
With different CSS properties, you couldn't choose the order you want. That's the limitation of CSS Transforms level 2 that BoltClock mentioned, the spec defines an order and you can't alter it.

The CSS transform property originated from SVG transforms, where a space-delimited list of transform functions is provided as a value for the SVG transform attribute. The CSS transform property is most likely a direct port of that.
Of course, hindsight has shown this to be a terrible mistake, and the transform functions will be promoted to their own CSS properties in CSS Transforms level 2, with almost the exact syntax that you have proposed (there aren't individual scaleX/Y/Z properties yet). Their interaction with the transform property is accounted for, although the draft notes that the transformation matrix will be changed to accommodate how the new properties will interact with respect to the cascade.

I agree that the option with individual would be nice, as you for example would be able to manipulate them with ease with Javascript. The probable reason that this isn't the case is that the order of the declarations matter with transform. The axes on which the element moves change when you rotate the element etc.
/* Transform */
.translate {
transform: translateX(200px) rotateZ(90deg);
}
.rotate {
transform: rotateZ(90deg) translateX(200px);
}
/* Demo */
div {
width: 100px;
height: 100px;
background-color: red;
margin-bottom: 10px;
position: relative;
}
div.translate:before {
position: absolute;
display: block;
content: '';
width: 100px;
height: 200px;
top: 100px;
left: 0;
border:2px dashed #333;
border-top:none;
box-sizing:border-box;
}
div.rotate:before {
position: absolute;
display: block;
content: '';
width: 200px;
height: 100px;
top: 0;
left: -200px;
border:2px dashed #333;
border-right:none;
box-sizing:border-box;
}
<div class="translate">
</div>
<div class="rotate">
</div>

Related

Why pseudo element background color covered its parent background color when used with transform? [duplicate]

This question already has answers here:
I have position but z index is not working
(1 answer)
Why can't an element with a z-index value cover its child?
(5 answers)
Closed 3 years ago.
UPDATE: This question was marked as duplicate by a friend, but I think the answer is still very valuable. I looked into those answers in duplicate questions and no one mentioned transform-style: preserve-3d can do transform without creating new stacking context. So this problem is more specific than how z-index works. It's also about how transform works.
I was trying to add some animation when hovering over a div element. But when I added transform in hover, its pseudo child element's background color covered div's. It seems that this wired behavior only happens when using transform. I want to know what's the mechanism behind this behavior.
In the following codepen example, the first one is hover with transform, the second one is normal hover.
https://codepen.io/neirongkuifa/pen/PgaEZd
.container {
width: 100px;
height: 100px;
background-color: red;
position: relative;
margin-bottom:100px;
}
.move:hover {
transform: translateY(3px);
}
.changeColor:hover{
background-color:white
}
.container::after {
content: '';
display: inline-block;
position: absolute;
top: 0;
left: 0;
background-color: green;
width: 150px;
height: 150px;
z-index: -1;
}
<div class="container move">Content</div>
<div class="container changeColor">Content</div>
You are creating a new stack context, and the z-index behaves differently.
Your best solution is to handle everything using transforms. I have added a transformZ negative in the pseudo to move it backwards, and a preserve-ed on the item to make this work:
.container {
width: 100px;
height: 100px;
background-color: red;
position: relative;
margin-bottom:100px;
transform-style: preserve-3D; /*added*/
}
.move:hover {
transform: translateY(3px);
}
.changeColor:hover{
background-color:white
}
.container::after {
content: '';
display: inline-block;
position: absolute;
top: 0;
left: 0;
background-color: green;
width: 150px;
height: 150px;
z-index: -1;
transform: translateZ(-1px); /*added*/
}
<div class="container move">Content</div>
<div class="container changeColor">Content</div>

Hover not reliable in a relative hierarchy using CSS rotation

I'm trying to create a online card game, using pure HTML/CSS.
I created a relative hierarchy of objects and I want the user to interact with them.
Probleme is, with CSS rotations (transform: rotateX, transform-style: preserve-3d), hover is not reliable.
Here's a simplified version of what it looks like :
http://jsfiddle.net/qLg9u51e/1/
Here are the main elements :
.container {
transform: rotateX(50deg);
transform-style: preserve-3d;
}
.tile {
position: relative;
}
.object {
position: absolute;
background: orange;
}
.object:hover {
background: red
}
I am expecting the orange object to be red while the mouse is hovering it, but as you can see, that's not always the case. It's a weird behaviour and I do not fully understand it.
By removing either rotateX, preserve-3d or the relative property, the hover property works correctly, but I need these elements.
Why am I doing wrong here ? And if you don't know how to solve my problem, do you know why CSS is acting like this ?
It looks like the row was overlapping the object at some points (not all, which is a bit confusing!).
Adding .row { pointer-events: none; }and .object { pointer-events: all; } fixes the problem:
.master {
perspective: 500px;
width: 200px;
height: 100px;
}
.container {
transform: rotateX(50deg);
transform-style: preserve-3d;
}
.row {
width: 200px;
background: darkgray;
padding: 20px;
pointer-events: none;
}
.tile {
height: 150px;
width: 80px;
margin-left: 60px;
margin-right: 60px;
background: #505050;
position: relative;
}
.object {
position: absolute;
height: 140px;
width: 70px;
margin: 5px;
background: orange;
pointer-events: all;
}
.object:hover {
background: red;
}
<div class="master">
<div class="container">
<div class="row">
<div class="tile">
<div class="object"/>
</div>
</div>
</div>
</div>
It's not ideal since I can't quite pinpoint the root cause, but it works in the meantime!

Making a odd-shaped elementwith css

I'm trying to make a odd-shaped dialogue box shaped liked one in the link below (without the wiggly tail). I've thought about doing it through polyfill, but that ended up making the element too large. I thought I could maybe do it with pseudo elements, but I didn't think I could put a shadow behind effect and have the odd-shaped top/bottom with just two pseudo elements.
The best method I could think of was to nest multiple divs inside the main one and position them with absolute and set the top manually, but I was wondering if there was an easier way of doing so.
If I want to make a div like this and write things inside without them being cut off, how should I go about doing it?
A single element with pseudo classes absolutely positioned using perspective and transforms can look like that.
div {
position: relative;
display: inline-block;
color: white;
margin: 3em;
perspective: 250px;
}
div::before, div::after {
content: '';
position: absolute;
z-index: -1;
top: -1em;
left: -1.5em;
right: -2em;
bottom: -1em;
background: black;
padding: 2em 3em .5em .5em;
transform: rotateX(180deg) rotateY(15deg) rotate(1.5deg) skewX(25deg);
}
div::before {
background: white;
top: -1.5em;
left: -2.25em;
right: -2.75em;
bottom: -1.75em;
transform: rotateX(180deg) rotateY(15deg) skewX(35deg);
}
body {
background: red;
}
<div>i know kung foo</div>

Css infinite loop animation bug

I see an annoying bug happening at the end of the animation loop, where it blinks for a fraction of a second, and makes the animation look choppy.
Here is the pen.
SCSS:
$dim: 60px;
$mult: 1.8;
$color: #bada55;
body, html {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #36a;
}
.circle {
background-color: $color;
border-radius: 50%;
width: $dim;
height: $dim;
position: relative;
}
.circle:before {
content: "";
display: table;
background-color: $color;
border-radius: 50%;
position: absolute;
animation: notification 800ms ease-in infinite;
}
#keyframes notification{
0% {
opacity: 1;
width: $dim;
height: $dim;
left: 0;
top: 0;
}
90% {
opacity: 0;
left: -(($dim * $mult) - $dim)/2;
top: -(($dim * $mult) - $dim)/2;
width: $dim * $mult;
height: $dim * $mult;
}
100% {
opacity: 0;
width: $dim;
height: $dim;
left: 0;
top: 0;
}
}
I've tried adding another frame, but it doesn't really remove it. I also tried hiding the before div afterwards, but doesn't work. Neither with z-index.
Any suggestions?
The problem with the "choppy" behaviour is: You change the dimensions and the positioning of your element. This forces the browser to re-render the element (in this case your :before pseudo-element. This makes the calculation for the browser way harder than it has to be.
Instead of alternating the dimensions, you could use a simple transform. Transforming elements does not force a re-rendering of the element and therefore performs way smoother. Also, it makes the code a bit easier as well. I forked your CodePen and used the transform instead of the dimensions: http://codepen.io/HerrBertling/pen/NbrPJb
It's certainly not perfect concerning the animation and the dimensions, but it should run way smoother.
(Check e.g. this Medium post for further info about the browser's behaviour: https://medium.com/outsystems-experts/how-to-achieve-60-fps-animations-with-css3-db7b98610108#.sykm5uqyv)

draw angular side / parallelogram using CSS

Need to draw angular sides of menubar as
inner content may be the some labels or links.
How about using CSS3 transform skew?
Demo
.shape {
width: 200px;
height: 50px;
-webkit-transform: skew(30deg);
-moz-transform: skew(30deg);
transform: skew(30deg);
background: #000;
margin: 20px;
}
Nothing much to explain here, it's a simple div element, which I've skewed by 30deg which will result in the shape you expected.
Note: It's a CSS3 property, so older browsers, as well as IE will spoil your things, make sure you use CSS3 Pie.
Other way to achieve this is by using :after and :before pseudo and CSS Triangles along with content property.
Demo 2 (Kept red triangles for demo purpose)
Demo 3 (Color Changed)
Demo 4 (As you commented, you need to use top: 0; for :before and :after pseudo as well, because when you add text, it will shift both the triangles from the top. So inorder to prevent that, use top: 0;)
Here, am using a simple div element and placing 2 CSS triangles which are positioned absolute to the container. This is more compatible than above, if you are going for a NON CSS3 solution, you can choose this. Make sure you use display: block; for :before as well as :after. And ofcourse you can merge the common styles but I've kept both separate, so that you can get easability to customize them separately.
.shape {
width: 200px;
height: 50px;
background: #000;
margin: 50px;
position: relative;
}
.shape:before {
display: block;
content: "";
height: 0;
width: 0;
border: 25px solid #f00;
border-bottom: 25px solid transparent;
border-left: 25px solid transparent;
position: absolute;
left: -50px;
}
.shape:after {
display: block;
content: "";
height: 0;
width: 0;
border: 25px solid #f00;
border-top: 25px solid transparent;
border-right: 25px solid transparent;
position: absolute;
right: -50px;
}
HTML
<div class="shape">
<div class="text">
text goes here
</div>
</div>
CSS
.shape {
width: 200px;
height: 30px;
-webkit-transform: skew(30deg);
-moz-transform: skew(30deg);
transform: skew(30deg);
background: #000;
margin: 20px;
color:#fff;
}
.text{
width: 150px;
height: 30px;
margin:0px auto;
-webkit-transform: skew(-30deg);
-moz-transform: skew(-30deg);
transform: skew(-30deg);
color:#fff;
}
One major gripe I have with using triangular borders is that there is no easy way to have multiple triangles with different colours, even using javascript [because JS can't access the pseudo-elements :before and :after], the alternative being that I use 3 divs, align them properly, and give all of them the same colour, etc... Too much hassle.
The best way would be using transform: skew() for newer browsers.
But you need to keep in mind that this will transform every element inside that div as well. So the text inside your menu-bar would also come up skewed. To counter that, add a reverse-skew on the inner div like this:
.menu-container {
...
transform: skewX(30deg);
...
}
.menu-inner {
...
transform: skewX(-30deg);
...
}
Have fun experimenting... :)

Resources