I noticed an unusual behavior when transform-style: preserve-3d is set and an element is rotated 180 degrees. It seems this causes the rotated element to go behind others, as if it has z-index set to a lower value.
Here's a sample:
.parent {
transform-style: preserve-3d;
position: relative;
}
.card {
position: absolute;
width: 100px;
height: 100px;
}
.card.red {
background-color: red;
}
.card.blue {
background-color: blue;
}
.rotated {
transform: rotateY(180deg);
}
<div class="parent">
<div class="card red"></div>
<div class="card blue rotated"></div>
</div>
When using Chrome, the (rotated) blue card is on top in HTML, but the red card is visible instead. Giving a z-index to the blue card also seems to have no effect.
I couldn't find it documented anywhere - is this a glitch or expected behavior? Is there a way to ensure the blue card is shown instead (while keeping transform-style: preserve-3d)?
Edit: it seems this behavior is not present in Firefox. However, it's unclear if this is an issue with Chrome or Firefox.
transform-style:preserve-3d means "the children of the element should be positioned in the (same) 3D-space". Which means if you apply 3d transformations to them they will be rendered accordingly.
Now, both your elements are at the same exact z-index value. Which is, by default, auto, which corresponds to 0.
But if you rotate the element by 180deg on Y or X axis, it will basically flip. Which means it will be at the other end of the same z-index value (because you set transform-style:preserve-3d). Which means it will be at the opposite end of the stack (from it's normal position - which is on top of its preceding siblings).
So you either give it specifically a bigger z-index or you switch their positions.
To understand more of this behavior play with the rotateY. You will notice at other values only half the element is visible, and that's the half that's closer to you, considering the rotation angle in 3d space.
At 90 and 270 degrees it will be invisible as it is perpendicular (and has no width) and at 180 it is "completely" rotated in 3d space, which means it's behind the other element that's at the same z-index value and (technically) "behind" it.
Another "fix" is to remove preserve-3d from the parent, (or move it to the child, as I.Johnson suggested), so the 2 elements are rendered each in its own space, and each representation of that space would be stacked according to their respective elements position in the stacking context.
When using the transform-style property it must be used together with the transform property.
Adding the transform-style: preserve-3d; property to the .rotated class displays the blue card
.rotated {
transform: rotateY(180deg);
transform-style: preserve-3d;
}
Example:
https://codepen.io/IanJohnson/pen/jXGVVo?editors=1100
because both cards has the same z-index
if you change your HTML to:
<div class="parent">
<div class="card red"></div>
</div>
<div class="parent">
<div class="card blue rotated"></div>
</div>
it will render the blue one
I think this is because the back of a DIV isn't necessarily the same color as the front. So rotating on the Y axis you're seeing the back/invisible/transparent side of the DIV. If you rotate on the Z axis you have the same face of the DIV visible.
Update: #fstanis pointed out that my transparent backing was incorrect. So if the Z vs. Y axis does still work as expected, then I don't actually 100% know why :)
.parent {
transform-style: preserve-3d;
position: relative;
}
.card {
position: absolute;
width: 100px;
height: 100px;
}
.card.red {
background-color: red;
}
.card.blue {
background-color: blue;
}
.rotated {
transform: rotateZ(175deg);
}
<div class="parent">
<div class="card red"></div>
<div class="card blue rotated"></div>
</div>
Related
I found strange behavior when coding css specifically using negative margin to stack element on other element.
I understand natural stack order that when elements overlap, later element always goes on top(not using relative, absolute positioning).
Question1: Why If former element has image element, later element go under the image?
Question2: Moreover, when later element has opacity other than 1, later element go over the former element (set back to natural order?)
HTML:
<div class="box sample1">
<img src="http://fillmurray.com/100/100" alt="">
</div>
<div class="box sample1-2">opacity: 1</div>
<div class="box sample1-3">
<img src="http://fillmurray.com/100/100" alt="">
</div>
<div class="box sample1-4">opacity: .9</div>
SCSS:
.box {
width: 100px;
height: 100px;
}
.child {
width: 80px;
height: 80px;
}
.sample1 {
background-color: yellow;
width: 300px !important;
}
.sample1-2 {
background-color: red;
margin-top: -40px;
.child {
// background-color: green;
}
}
.sample1-3 {
// opacity: .9;
width: 300px;
background-color: green;
}
.sample1-4 {
opacity: .9; //this changes stack order
background-color: red;
margin-top: -40px;
}
Demo:
https://jsfiddle.net/nori2tae/4w62t746/8/
Need a little explanation to this, thanks.
Question1: Why If former element has image element, later element go
under the image?
It's because within the same stacking context - inline-level elements (such as the image) are painted above non-inline-level elements (see this post)
This article has a nice image to sum up the stacking order of elements within the same stacking context:
Question2: Moreover, when later element has opacity other than 1,
later element go over the former element (set back to natural order?)
It's because a new stacking context is formed on an element with a opacity value less than 1.
From the spec: (bold mine)
If an element with opacity less than 1 is positioned, the ‘z-index’
property applies as described in [CSS21], except that ‘auto’ is
treated as ‘0’ since a new stacking context is always created.
See all this MDN article on stacking contexts.
Suppose you have a parent div that contains several normal children and one absolute child.
I've read practically everywhere that a child with position: absolute will not influence parent's height, since it is out of the normal flow. However in my case, an absolute element expands the parent, and I can't understand why.
(I tried reading the spec but I'm really lost.)
HTML
<div class="container">
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="outsider"></div>
</div>
CSS
.container {
overflow: hidden;
}
.block, .outsider {
width: 100%;
height: 1000px;
}
.block {
background: red;
}
.outsider {
position: absolute;
left: 0;
top: 3000px;
background: green;
opacity: 0.5;
}
Why does the browser let me scroll past the element's supposed height? It seems consistent in Chrome, Safari and Firefox so I presume it's part of the spec.
How do I prevent this behavior? I'd like absolutely positioned element to be cropped if it doesn't fit into the container height “dictated” by “normal” children.
See it live.
You are missing a position on your parent container. Add
.container{
position: relative;
}
The absolutely positioned element will go back up the DOM to find the nearest positioned parent, in this case you don't have one explicitly defined, so it's going back up to <body>
I have two containers, each contains a nested div structure that are having the 3d transformations applied to them. The effect is to simulate a piece of paper being unfolded down the centre line.
You see the current state of the code in this JSFiddle.
The problem that I have encountered is attempting to reverse the right had animation on the left.
I've achieved what I have already by placing each starting segment with translate3d().
The HTML structure is as follows.
<div class="container">
<div class="left">
<div class="slice s1">
<div class="slice s2"></div>
</div>
</div>
<div class="right">
<div class="slice s1">
<div class="slice s2"></div>
</div>
</div>
</div>
The .left and .right elements are positioned absolutely within .container and set to left:50% to centre them. The first element of .left is then given a negative left:-100px to position it correctly.
.container {
position:absolute;
margin-left:-200px;
left:50%;
}
.left, .right {
width: 200px;
-webkit-perspective: 500px;
-moz-perspective: 500px;
perspective: 500px;
position: absolute;
}
.left {
left: -100px;
}
.right {
left:0;
}
The transform3d() then places each segment either 99px to the right or 99px to the left depending on their container.
If you see the JSFiddle , you'll notice that the angles are off on the left hand portion. I am not sure how to fix this to make it all evened out and symmetrical.
The 3d planes are not right as demonstrated in this inspection in Chrome:
Can anyone help guide me to making this symmetrical?
The 2 sides are symetrical, it's your point of view that isn't.
Try:
.right {
left:0;
-webkit-perspective-origin: 0px 150px;
perspective-origin: 0px 150px;
}
And will solve it.
An even better approach would be to have the left and right sides have the size that you see (they are bigger on the right).
That would make the perspective point by default (center center) to be also symetrical, and wouldn't need to set it to an arbitrary value
I'm building a strange div shaped structure and I need a hint to resolve a clicking problem.
This is a jsfiddle to show you the issue.
The structure for each element is:
<div class="views-row">
<div class="diamonds-container">
CONTENT
</div>
</div>
I have a onclick() event on .diamonds-container but the .views-row div of the next element [with red or blue background..] go over the container and stop the click event on it.
I tryed to play with the z-index but I didn't have the expected result.
How can I achieve this structure with a correct click event on diamonds-containers ?
I think I can track the .views-row click with javascript and trigger manually a click on the previous diamonds-container but this will be my final option.
How can I achieve this without javascript?
UPDATE:
I have to position my diamonds like this
so I can't use the #matewka code because I will have the overlaping vertically instead of orizzontally..
There is more than one route for this kind of problem.
If you use the rotation transform anyway, why not rotate the .views-row element to get the bounding box out of the way?
For recent browsers and IE11 there are pointer events. See this updated fiddle.
.views-row {
z-index: 1;
pointer-events: none;
}
.diamonds-container {
z-index: 9;
pointer-events: auto;
}
Here is my approach. I'm not sure if nesting two divs inside each other was for rotating purpose or had some other meaning. Anyway, I did it this way:
.views-row {
width: 130px;
height: 130px;
-webkit-transform: rotate(45deg);
}
.views-row-first {
-webkit-transform-origin: 195px center;
}
.views-row-even {
-webkit-transform-origin: center center;
}
.views-row-odd {
-webkit-transform: rotate(-45deg);
-webkit-transform-origin: -65px center;
}
Each .views-row is rotated and the transform origins are all pointed to the center of the middle div. Notice that the transform-origin values are multiplicities of the half of the width (130px / 2).
See the updated FIDDLE for the complete CSS. I also added a :hover property for .diamonds-container so you can see that they're all clickable.
UPDATE
With the picture you added the problem became much more complicated. But I figured it out.
Hint: If you can't wait for the fiddle - you'll find it at the bottom of the answer.
The idea:
Square boxes are nested twice. Each 2 .diamond boxes are wrapped with the .pair-wrapper div. That div is rotated 45deg and it is repeated few times along its container. Each even .pair-wrapper has increased width to position its right-hand neighbour properly.
A bunch of .pair-wrappers are wrapped with the .line-wrapper. You can add as much .line-wrappers and .pair-wrapper as you want (remember - .pair-wrappers will break into the new line if they don't fit).
Finally, each .line-wrapper has fixed height and hidden overflow to restrict its children area from the top and the bottom. Each .pair-wrapper is positioned relatively and has negative top value.
The solution is based mostly on fixed values, both I could figure out a better idea.
The code
Example HTML markup looks like this:
<div class="line-wrapper line-wrapper-odd">
<div class="pair-wrapper pair-wrapper-odd">
<div class="diamond-box"></div>
<div class="diamond-box"></div>
</div>
<div class="pair-wrapper pair-wrapper-even">
<div class="diamond-box"></div>
<div class="diamond-box"></div>
</div>
<div class="pair-wrapper pair-wrapper-odd">
<div class="diamond-box"></div>
<div class="diamond-box"></div>
</div>
</div>
<div class="line-wrapper line-wrapper-even">
<div class="pair-wrapper pair-wrapper-odd">
<div class="diamond-box"></div>
<div class="diamond-box"></div>
</div>
.....
</div>
.....
And the most important parts from CSS (complete CSS in the fiddle):
.line-wrapper {
height: 170px;
overflow: hidden;
}
.line-wrapper-even {
margin-left: -92px;
}
.pair-wrapper {
width: 130px;
position: relative;
top: -26px;
-webkit-transform: rotate(45deg);
}
.pair-wrapper-odd {
-webkit-transform-origin: 65px 65px;
}
.pair-wrapper-even {
-webkit-transform-origin: 92px 131px;
width: 239px;
}
.diamond-box {
width: 130px;
height: 130px;
}
The fiddle
http://jsfiddle.net/N3V6J/3/
I'm really confused how scaling an element using css transforms affects document flow.
Consider this jsbin or this codepen since jsbin seems to have gone down where I have
p{slipsum text}
#scaled
#scaled-content{some text}
p{slipsum text}
with the stylesheet
#scaled-contents {
height: 400px;
width: 400px;
background-color: blue;
color: red;
font-size: 3em;
}
#scaled {
transform: scale(0.25, 0.25); //browser prefixes...
width: 100px;
height: 100px
}
I would expect this to show up similarly to a single 100x100 blue square. But it is shifted and on chrome even overlaps the following p element slightly. In addition, examining the dimensions of #scaled in devtools shows at as squat and long, seemingly breaking beyond it's 100x100 box.
Finally, adding overflow: hidden; to #scaled does something crazy altogether.
What is going on? How is content flow supposed to be affected?
CSS Transform does not affect document flow. The DOM element will occupy it's original position and dimensions within the page flow.
So if you have 3 square div's of identical size, displayed inline in a row and apply a -webkit-transform: scale(2) to the center square, this square will scale up to 200% larger, scale from the center of its original position, and overlap both other squares.
Reference example:
http://jsfiddle.net/ypnEk/
HTML:
<div class="square one"></div>
<div class="square two"></div>
<div class="square three"></div>
CSS:
.square{
margin-top:50px;
width:50px;
height:50px;
display:inline-block;
}
.one{
background:#222;
}
.two{
background:#888;
-webkit-transform: scale(2);
}
.three{
background:#ccc;
}