Hover not reliable in a relative hierarchy using CSS rotation - css

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!

Related

CSS Animation: Different color for Width-Animation

I have a DIV with "width: 100%", and after some time it moves to a different width (for example "width: 50%") with a nice transition. I want that the part which is going to decrease to get a different color. How do I do it?
.container {
width: 80%;
height: 50px;
}
.bar {
background-color: red;
width: 100%;
height: 100%;
transition-duration: 1s;
}
<div class="container">
<div class="bar"></div>
</div>
You can simply put another bar exactly behind this bar. Set it's z-index to 1. And your current bar's z-index to a higher number.
The bar that is in the back, has a black color like you wanted.
Now when the red bar decrease it's width, the one in the back becomes visible.
Here you go, the most basic working example.
(Try hovering the bar and see it moving)
By simply overlaying a pseudo-element over the static .bar element, with position:absolute, it is places above the bar and has dynamic width which you should change.
.container {
width: 80%;
height: 50px;
}
/* static background bar */
.bar {
--progress: 80%; /* css variable */
position: relative;
background-color: salmon;
width: 100%;
height: 100%;
}
/* This is the part which moves: */
.bar::before{
content: '';
background-color: darkred;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
width: var(--progress);
transition: 1s;
}
.bar:hover {
--progress: 20%;
}
<div class="container">
<div class="bar"></div>
</div>
This solution uses a linear-gradient where I animate the background-size. I placed a fully black gradient on the .container, and a red one on the .bar, where I put a transition: background-size 1s on the black .container.
By using javascript to change a CSS variable, I can get the bar to animate.
Added a range to show-case the result.
Do note that it's always better to specify what attribute to animate with transiation, because otherwise the browser loops through all animatable properies, which can create janky animations.
let rangeInput = document.querySelector('input[type="range"');
let containerDiv = document.getElementById("container");
if (rangeInput) {
rangeInput.addEventListener('change', (event) => {
let newValue = event.target.value;
containerDiv.style.setProperty('--bar-width', `${newValue}%`);
});
}
input[type="range"] {
width: 80%;
margin-top: 1rem;
}
#container {
--bar-width: 100%;
width: 80%;
height: 50px;
background: linear-gradient(black, black);
transition: background-size 1s;
}
/* added "div" to obtain a higher specificity, to override the default background properties. */
div#container,
div.bar
{
background-repeat: no-repeat;
background-size: var(--bar-width);
}
.bar {
background: linear-gradient(red, red);
width: 100%;
height: 100%;
}
<div id="container">
<div class="bar"></div>
</div>
<input type="range" value="100" />

Why are "transform" properties grouped?

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>

How do you affect multiple child divs when hovering on the parent

I am trying to implement some CSS on my website that I am having a little bit of difficulty with. I currently have five 'balls' (which are just circular divs) that have staggered heights. I have been experimenting with having the balls move around when you hover on them, which is great, but currently they only continue to move as long as you keep your pointer trained on it as it moves.
Ideally, I would like to have all five move independently when you hover in the general area. I have enclosed them in a wrapper div, but I am unsure of the code to affect the child divs when you hover on the parent. I am also not sure if I'm using the terms parent and child correctly, as I've only come across this concept in the last 20 minutes!
Here is the HTML:
<div id='demoStrip'><div id='ballWrapper'>
<div id='bounce'>
<div class='ball' id='ball1'><p>Professional</p></div>
<div class='ball' id='ball2'><p>Copy</p></div>
<div class='ball' id='ball3'><p>Just</p></div>
<div class='ball' id='ball4'><p>For</p></div>
<div class='ball' id='ball5'><p>You</p></div>
</div>
</div></div> <!-- End of demoStrip div -->
Here is the CSS as it currently stands:
#demoStrip {
width: 960px;
height: 410px;
margin: 20px auto 0 auto;
/*background: #00cccc;*/
border-radius: 20px;
}
#ballWrapper {
width: 900px;
height: 410px;
margin: 0 auto 0 auto;
}
.ball {
margin: 0 20px 0 20px;
/*width: 150px;
height: 150px;*/
border-radius: 200px;
background-image: radial-gradient(circle closest-corner at center, #FFFF99 0%, #FFFF00 100%);
float: left;
box-shadow: 5px 5px 5px #333333;
}
.ball p {
text-align: center;
text-transform: uppercase;
font-family: sans-serif;
margin: 0;
}
#ball1 {
width: 150px;
height: 150px;
margin-top: 245px;
}
#ball2 {
width: 140px;
height: 140px;
margin-top: 185px;
}
#ball3 {
width: 130px;
height: 130px;
margin-top: 125px;
}
#ball4 {
width: 120px;
height: 120px;
margin-top: 65px;
}
#ball5 {
width: 110px;
height: 110px;
margin-top: 5px;
}
#ball1:hover {
margin-top: 5px;
transition: margin-top 3s;
}
#bounce:hover ~ #ball2:hover {
margin-top: 65px;
transition: margin-top 3s;
}
The very last bit of code is my attempts to make ball2 'bounce' when hovering on the 'bounce' div. At the moment it doesn't work, but I'm sure syntactically it is all wrong. Any advice would be much appreciated!
Write your CSS so it affects the balls inside a hovered div:
#bounce:hover .ball {
...
}
Or, if you need different CSS for each ball:
#bounce:hover #ball1 {
...
}
#bounce:hover #ball2 {
...
}
...
Give the hover a 100% width/ height
#ball1:hover {
width:100%;
height:100%
margin-top: 5px;
transition: margin-top 3s;
}
#bounce:hover ~ #ball2:hover {
margin-top: 65px;
transition: margin-top 3s;
width:100%;
height:100%
}
Ideally, I would like to have all five move independently when you
hover in the general area...
So do something like this:
#demoStrip:hover #bounce .ball:nth-child(1) {
//animation here
}
This should fix your second issue of...
currently they only continue to move as long as you keep your pointer
trained on it as it moves
If you still run into that issue then set them both to position: relative and set each element's z-index, but be sure to set the .ball to a higher z-index. See Submenu does not stay open for more on why.
Your last attempt wasn't too far off, but instead of
#bounce:hover ~ #ball2:hover {...}
use
#bounce:hover #ball2 {...}
this will
make ball2 'bounce' when hovering on the 'bounce' div
You do not need the second :hover on #ball2, nor do you need the tilde (~).
In CSS the tilde (~) symbol is the 'general sibling' combinator. See this article for a great intro to CSS selectors/combinators http://code.tutsplus.com/tutorials/the-30-css-selectors-you-must-memorize--net-16048
You could use the tilde to hover #ball1 and animate #ball2 (providing #ball2 is after #ball1 in the DOM) like so:
#ball1:hover ~ #ball2 {
margin-top: 65px;
transition: margin-top 3s;
}

Prevent elements clipping when using 3D CSS transforms

I am having trouble preventing two elements clipping when using 3D CSS transforms. Has anyone come across this before and found a solution?
I have attached a screenshot from the latest version of iOS to illustrate the issue - It also occurs on the desktop version of Safari, but not Chrome on OS X.
I understand why this happens, and even that this is the correct behaviour in some circumstances, but it is inconsistent across different browsers.
Thanks for any help :)
This is caused by rendering both elements within the same 3d layer. The solution is to render them each in their own layer.
This is a simplified version of the code which caused the issue:
CSS:
.wrapper {
transform-style: preserve-3d;
perspective: 1000;
}
.rotate {
width: 100px;
height: 100px;
background: grey;
transform: rotateX(45deg);
}
.clipped-element {
width: 30px;
height: 30px;
background: blue;
}
HTML:
<div class="wrapper">
<div class="rotate">
<div class="clipped-element"></div>
</div>
</div>
By using transform-style and perspective I've created a rendering layer. As the .clipped-element is part of this layer it exists in the same 3d space.
By moving the clipped element into it's own layer it exists in it's own 3d space and the clipping issue is avoided.
CSS:
.wrapper {
width: 200px;
height: 200px;
}
.rotate__wrapper {
width: 100%;
height: 100%;
transform-style: preserve-3d;
perspective: 1000;
}
.rotate {
width: 100px;
height: 100px;
background: grey;
transform: rotateX(45deg);
}
.clipped-element {
width: 30px;
height: 30px;
background: blue;
}
HTML:
<div class="wrapper">
<div class="rotate__wrapper">
<div class="rotate"></div>
</div>
<div class="clipped-element"></div>
</div>
I've created an example on CodePen.

Triangular css button image

I'm trying to make a splash page on my website with 2 large buttons, each a right angled triangle, and both join by the longest side to create a square. Basically I'm looking to find out how to make non-rectangular buttons in css.
I have no idea if this is even possible though, and cannot find anything online explaining similar techniques for buttons which are not rectangular, and i'm not particularly skilled in css. A push in the right direction would be very helpful!
A very old (unanswered question) deserves an answer.
You could use a nested div element in which the parent has an overflow set to hidden, with the child element rotated.
Here is a basic example: (please note: jQuery only required for demo)
$('.tri').click(function() {
alert("triangle clicked!");
});
.wrap {
height: 200px;
width: 200px;
position: relative;
overflow: hidden;
margin: 2px auto;
}
.wrap .tri {
position: absolute;
height: 70%;
width: 70%;
background: tomato;
transform-origin: bottom left;
bottom: 0;
transition: all 0.6s;
left: 0;
cursor: pointer;
transform: rotate(45deg);
}
.wrap2 {
transform: rotate(180deg);
}
.wrap .tri:hover {
background: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrap">
<div class="tri"></div>
</div>
<div class="wrap wrap2">
<div class="tri"></div>
</div>

Resources