Maintain alignment with scaled sibling without inverse-scaling - html

I have an element that requires the background to be scaled, without scaling the elements within the parent. I have achieved this by using a pseudo element to define the background, and then on hover I simply scale the pseudo element. So far, so good...
The problem is, I need some of the elements to stay inline with the scaled background, despite not scaling themselves. My original plan was to simply translate them, but I quickly realised that is not possible due to scale being based on multiples, and translate being based on percentage/pixels etc...
The obvious solution is to scrap scale and instead use margin to shrink the absolutely positioned pseudo element. However, my reservation with this is that it is bad practice to transition the margin value.
Can anybody think of a way in which I can use scale, and also maintain the alignment?
Update
I want to avoid inverse/reverse scaling at all costs as it renders badly in the browser in most cases. With that in mind, I don't think this is actually possible but will leave the question open in case anyone is aware of some CSS magic.
See the following snippet as an example:
.tile {
position: relative;
width: 500px;
height: 100px;
padding: 40px;
}
.tile:hover:before {
transform: scale(.9);
}
.tile:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
transition: transform .3s ease-out;
}
h1 {
text-align: center;
color: white;
}
.tile > .button {
position: absolute;
right: 0;
top: 0;
display: inline-block;
background: red;
padding: 10px 15px;
color: white;
}
<div class="tile">
<h1>Hello World</h1>
<div class="button">Align Me</div>
</div>

Try scaling .tile itself and reverse-scaling its children:
.tile {
position: relative;
width: 500px;
padding: 40px;
background: #000;
transition: transform .3s ease-out;
}
h1 {
text-align: center;
}
.tile>* {
color: white;
transition: transform .3s ease-out;
}
.tile>.button {
position: absolute;
right: 0;
top: 0;
background: red;
padding: 10px 15px;
color: white;
transform-origin: 100% 0;
}
.tile:hover {
transform: scale(.9);
}
.tile:hover>* {
transform: scale(1.1);
}
<div class="tile">
<section>
<h1>Hello World</h1>
<p>I have an element that requires the background to be scaled, without scaling the elements within the parent. I have achieved this by using a pseudo element to define the background, and then on hover I simply scale the pseudo element. So far, so good...
The problem is, I need some of the elements to stay inline with the scaled background, despite not scaling themselves. My original plan was to simply translate them, but I quickly realised that is not possible due to scale being based on multiples,
and translate being based on percentage/pixels etc... The obvious solution is to scrap scale and instead use margin to shrink the absolutely positioned pseudo element. However, my reservation with this is that it is bad practice to transition the
margin value. Can anybody think of a way in which I can use scale, and also maintain the alignment?</p>
</section>
<div class="button">Align Me</div>
</div>
Another idea is animating top and right of .button:
html,
body {
width: 75%;
margin: 0;
padding: 0
}
* {
box-sizing: border-box
}
.tile {
position: relative;
width: 100%;
padding: 40px;
color: white;
}
.tile:hover:before {
transform: scale(.9);
}
.tile:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
transition: transform .3s ease-out;
}
h1 {
text-align: center;
}
.tile>.button {
position: absolute;
right: 0;
top: 0;
background: red;
padding: 10px 15px;
color: white;
transition: .3s ease-out;
}
.tile:hover>.button {
top: 5%;
right: 5%
}
<div class="tile">
<h1>Hello World</h1>
<p>I have an element that requires the background to be scaled, without scaling the elements within the parent. I have achieved this by using a pseudo element to define the background, and then on hover I simply scale the pseudo element. So far, so good...
The problem is, I need some of the elements to stay inline with the scaled background, despite not scaling themselves. My original plan was to simply translate them, but I quickly realised that is not possible due to scale being based on multiples,
and translate being based on percentage/pixels etc... The obvious solution is to scrap scale and instead use margin to shrink the absolutely positioned pseudo element. However, my reservation with this is that it is bad practice to transition the
margin value. Can anybody think of a way in which I can use scale, and also maintain the alignment?</p>
<div class="button">Align Me</div>
</div>
The next idea is using a bit more complex code, but doing animation of transform property only:
html,
body {
width: 75%;
}
* {
box-sizing: border-box
}
.tile {
position: relative;
width: 100%;
padding: 40px;
color: white;
}
.tile:hover:before {
transform: scale(.9);
}
.tile:before,
.tile>.button {
content: '';
position: absolute;
top: 0;
left: 0;
z-index: -1;
width:100%; height:100%;
background: #000;
transition: transform .3s ease-out;
}
h1 {
text-align: center;
}
.tile>.button {
z-index: 1;
display: flex;
align-items: flex-start;
margin: 0 -100% -100% 0;
background: transparent;
transition: .3s ease-out;
pointer-events: none;
}
.tile>.button div {
padding: 10px 15px;
background: red;
cursor: pointer;
pointer-events: all;
}
.tile>.button:before {
content: '';
flex: 1 0;
}
.tile:hover>.button {
transform: translate3d(-5%, 5%, 0);
}
<div class="tile">
<h1>Hello World</h1>
<p>I have an element that requires the background to be scaled, without scaling the elements within the parent. I have achieved this by using a pseudo element to define the background, and then on hover I simply scale the pseudo element. So far, so good...
The problem is, I need some of the elements to stay inline with the scaled background, despite not scaling themselves. My original plan was to simply translate them, but I quickly realised that is not possible due to scale being based on multiples,
and translate being based on percentage/pixels etc... The obvious solution is to scrap scale and instead use margin to shrink the absolutely positioned pseudo element. However, my reservation with this is that it is bad practice to transition the
margin value. Can anybody think of a way in which I can use scale, and also maintain the alignment?</p>
<div class="button">
<div>Align Me</div>
</div>
</div>

If you are scaling by p then you are reducing the size and the new width will become width*(1 - p). Same logic for the height. You can consider the use of calc() and easily define the translate using this formula.
We divide by 2 because we reduce from both side and we will translate from 1 side
.tile {
position: relative;
width: 540px;
height: 200px;
display:flex;
align-items:center;
justify-content:center;
}
.tile:hover:before {
transform: scale(0.9);
}
.tile:hover .button{
transform: translate(calc(-540px*0.1/2),calc(200px*0.1/2));
}
.tile:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
transition: transform .3s;
}
h1 {
text-align: center;
color: white;
margin:0;
}
.tile > .button {
position: absolute;
right: 0;
top: 0;
display: inline-block;
background: red;
padding: 10px 15px;
color: white;
transition: transform .3s ;
}
<div class="tile">
<h1>Hello World</h1>
<div class="button">Align Me</div>
</div>
You can consider CSS variables to easily change the scale value:
.tile {
position: relative;
width: 540px;
height: 200px;
display:flex;
align-items:center;
justify-content:center;
--s:0.9;
}
.tile:hover:before {
transform: scale(var(--s));
}
.tile:hover .button{
transform: translate(calc(-540px*(1 - var(--s))/2),calc(200px*(1 - var(--s))/2));
}
.tile:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #000;
z-index: -1;
transition: transform .3s;
}
h1 {
text-align: center;
color: white;
margin:0;
}
.tile > .button {
position: absolute;
right: 0;
top: 0;
display: inline-block;
background: red;
padding: 10px 15px;
color: white;
transition: transform .3s ;
}
<div class="tile">
<h1>Hello World</h1>
<div class="button">Align Me</div>
</div>
<div class="tile" style="--s:0.5">
<h1>Hello World</h1>
<div class="button">Align Me</div>
</div>

Related

Exclude an div element from background overlay

Im trying to make a block with overlay hover effect (default: weak black background color - Hover: none black background) and an icon and text in the middle that stays in the same state all the way.
How do i get the icon and text to stay in the same state (no hover effect)?
Ive tried several rules to the overlay div and the icon div without any luck.
Is there any css rule that provide some kind of exclusion?
I managed to get it to work by adding them outside the divs that has overlay background, but it didnt work out well as the hover effect breaks when you hover over the icon and text.
Here is the code: https://www.w3schools.com/code/tryit.asp?filename=FEMUM4N9T30Q
<style>
.media-front-top-picture{
background-image: url("");
height:500px;
}
.media-front-top-icon{
content: url(");
width: 130px;
margin: auto;
padding-top: 200px;
opacity: 1;
}
.media-front-txt{
font-size: 22px;
letter-spacing: 8px;
color: white;
margin-top: 15px;
}
.media-front-bottom-picture{
background-image: url("h");
height:500px;
}
.media-front-bottom-icon{
content: url("");
width:130px;
margin: auto;
padding-top: 200px;
}
.media-picture-container {
position: relative;
}
.media-picture-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
transition: .5s ease;
background-color: rgba(0, 0, 0, 0.47);
}
.media-picture-overlay:hover {
opacity: 0;
cursor:pointer;
}
</style>
<div class="body-media">
<div class="media-picture-container">
<div class="media-front-top-picture" style="border-bottom:4px solid white;">
<div class="media-front-top-icon"></div>
<div class="media-front-txt">VIDEOS</div>
<div class="media-picture-overlay"></div>
</div>
</div>
<div class="media-picture-container">
<div class="media-front-bottom-picture" style="border-bottom:4px solid white;">
<div class="media-front-bottom-icon"></div>
<div class="media-front-txt">PICTURES</div>
<div class="media-picture-overlay"></div>
</div>
</div>
</div>
for the classes on your icons, add a z-index higher than a z-index you add to the overlay class. Also, make sure to make the icon classes have position:relative so the z-index is applied. Note, my example only applies this solution to one icon, its up to you to apply it elsewhere.
Example:
.media-front-top-icon{
content: url("example.com");
width: 130px;
margin: auto;
padding-top: 200px;
opacity: 1;
z-index:10;
position:relative;
}
.media-picture-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
transition: .5s ease;
background-color: rgba(0, 0, 0, 0.47);
z-index:5;
}

CSS: Reduce element size on hover, but keep original "hover area"

I want to reduce element size a bit as an effect, when it is hovered over with a mouse. However, this looks buggy because as the element reduces in size, the "hover area" gets smaller as well, which can result into the element not being hovered anymore, which further results into this "size flickering".
Is there a proper way to implement element size reduction on hover, while keeping the hover area size the same? Without extra elements?
Here is a fiddle: https://jsfiddle.net/ahvonenj/88f5by59/
Required code for fiddle linking:
#di
{
position: absolute;
left: 15px;
top: 15px;
background-color: #2980b9;
box-sizing: border-box;
width: 100px;
height: 100px;
}
#di:hover
{
width: 50px;
height: 50px;
transition: all 200ms linear;
}
Wrapping it in a div would be better, as commented. But if adding no other elements is a must, you could work with pseudo elements.
Make the visible part a pseudo element (like :before), and keep the main one just for hovering:
TIP: If you want the transition effect on both mouse over and out, set the property to the main css rule, not to the hover one
#di
{
position: absolute;
left: 15px;
top: 15px;
box-sizing: border-box;
width: 100px;
height: 100px;
}
#di:before {
content: "";
display: block;
background-color: #2980b9;
width: 100px;
height: 100px;
}
#di:hover:before
{
width: 50px;
height: 50px;
transition: all 200ms linear;
}
<div id = "di">
</div>
You can wrap the div inside a container and "bind" the hover event to the parent.
P.S obviously it is a solution with adding other elements.
#container
{
position: absolute;
left: 15px;
top: 15px;
box-sizing: border-box;
}
#container, #di{
width: 100px;
height: 100px;
}
#di{
background-color: #2980b9;
}
#container:hover #di
{
width: 50px;
height: 50px;
transition: all 200ms linear;
}
<div id="container">
<div id="di">
</div>
</div>
Yep, this is your answer. You have to add one more element. See this fiddle:
https://jsfiddle.net/vwy4utf5/
html:
<div id = "di">
<div id="diin">
</div>
</div>
CSS
#di{width:101px; height:101px; cursor:pointer; position: absolute;
left: 15px;
top: 15px;}
#diin
{
background-color: #2980b9;
box-sizing: border-box;
width: 100px;
height: 100px;
transition: all 200ms linear;
}
#di:hover > div
{
width: 50px;
height: 50px;
transition: all 200ms linear;
}
I tried it using Jquery, didn't specified by OP but I guess it can help somebody.
So changed css to make parent positioning of new parent:
#di {
background-color: #2980b9;
box-sizing: border-box;
width: 100px;
height: 100px;
}
#di_parent {
padding: 0;
overflow: hidden;
position: absolute;
left: 15px;
top: 15px;
}
#di_parent:hover > DIV {
width: 50px;
height: 50px;
transition: all 200ms linear;
}
Then added some JQuery to create a container to each object to maitain size as is suggested above.
$('#di').each(function(i, v){
var o, p;
o=$(v);
p=$('<div id="di_parent"></div>');
p.css({height:o.outerHeight(),width:o.outerWidth()});
o.after(p);
p.append(o.detach());
});
Working fiddle:
https://jsfiddle.net/88f5by59/11/
$('#di').each(function(i, v){
var o, p;
o=$(v);
p=$('<div id="di_parent"></div>');
p.css({height:o.outerHeight(),width:o.outerWidth()});
o.after(p);
p.append(o.detach());
});
#di {
background-color: #2980b9;
box-sizing: border-box;
width: 100px;
height: 100px;
}
#di_parent {
padding: 0;
overflow: hidden;
position: absolute;
left: 15px;
top: 15px;
}
#di_parent:hover > DIV {
width: 50px;
height: 50px;
transition: all 200ms linear;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id = "di">
</div>
Hope it helps!

Move div with dynamic height out of its parent container

I'm trying to move a div with a dynamically changing height out of it's parent div and back in.
The problem is the dynamically height, otherwise I could easily set the negative height as the bottom value.
For now I just set a large negative number of pixels as the bottom value, but it isn't very nice and does not solve the problem properly. (logically this happens for small numbers: fiddle)
Hopefully the example below clarifies what I try to do.
I was thinking about using transforms instead, but i did not find a solution as well.
Of course I could do this with JavaScript, but as everyone I prefer a pure CSS solution :)
#outer {
position: relative;
width: 400px;
height: 400px;
background: black;
overflow: hidden;
}
#inner {
position: absolute;
bottom: -500px;
/*
It's working but ugly and not perfect.
The value I need would be the height of the inner div, but it is dynamic
*/
width: 100%;
background: red;
transition: 0.4s;
}
#outer:hover #inner {
transition: 0.4s;
bottom: 0;
}
<div id="outer">
<div id="inner">
Some expanding text here
</div>
</div>
You could use CSS transform:translateY(100%) property, so the height is calculated based on the element itself. Then reset the value to 0 on hover.
Inspect the element, you'll be able to see exact the height and position of it.
Also take a look of support tables for transform, and prefix it if necessary.
Updated JsFiddle
.outer {
position: relative;
display: inline-block;
width: 100px;
height: 100px;
background: grey;
overflow: hidden;
}
.inner {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
background: aqua;
transition: 0.4s;
transform: translateY(100%);
}
.outer:hover .inner {
bottom: 0;
transform: translateY(0);
}
<div class="outer">
<div class="inner">Some expanding text here..</div>
</div>
If I understand your issue, you can set a max-height for its normal and :hover state and transition it. However, you must set it to a max-height that you know will always be tall enough (which may lead to random speeds depending on how much content there is).
So something like: JS Fiddle
.outer {
position: relative;
display: inline-block;
width: 400px;
height: 400px;
background: black;
overflow: hidden;
}
.inner {
position: absolute;
bottom: 0px;
width: 100%;
background: red;
transition: 0.4s;
max-height: 0;
overflow: hidden;
}
.outer:hover .inner {
transition: 0.4s;
bottom: 0;
max-height: 40px;
}
Otherwise, I would recommend a JS solution.

why is `position:relative` functioning like `z-index`?

Hey guys I am relatively very new to HTML and CSS and have the following difficulty I made a small input box and I am trying to add a few CSS transforms and create a small animation on the input box. Code below:
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
}
.input {
position: relative;
display: inline-block;
max-width: 350px;
width: 100%;
}
.akira-input {
position: absolute;
top: 0;
left: 0;
display: block;
height: 100%;
width: 100%;
background: transparent;
z-index: 10;
}
.akira-label {
display: block;
height: 100%;
padding: 0;
width: 100%;
background: #696a6e;
color: #cc6055;
cursor: text;
}
.akira-label:before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: red;
-webkit-transform: scale3d(0.97, 0.50, 1);
transform: scale3d(0.97, 0.50, 1);
-webkit-transition: .3s;
-o-transition: .3s;
transition: .3s;
}
.label-content {
color: #000;
font-size: 1.3em;
text-transform: uppercase;
position: relative;
display: block;
text-align: center;
padding: 1.6em 0;
width: 100%;
-webkit-transition: .3s;
-o-transition: .3s;
transition: .3s;
}
<span class="input">
<input type="text" id="akira" class="akira-input">
<label for="akira" class="akira-label">
<span class="label-content">Akira</span>
</label>
</span>
My difficulty is, if I apply position:relative to <span class="label-content">Akira</span>, it shows, if I remove position:relative , that element disappears from view.
My question is why is position:relative functioning like z-index?
Can somebody elaborate ??
EDIT :: refering to Justinas answer , i have the folloing question ,
Does applying position:relative places an element
higher in the stack , even without applying z-index ??
z-index is only working for non-static elements, so when you remove position: relative than element becomes statically positioned and moves below higher index elements (disappears from view). When you add position: relative to element, than z-index will take effect and so element appears in your view.
Also position and z-index is two different properties
position - how element is positioned according to other elements on page. Default to static
z-index - how high element is in z-axis (z-index: 2 - is behind element with z-index: 10). Default to 5
.wrapper {
position: relative;
}
#static {
position: static;
z-index: 999;
width: 400px;
height: 150px;
background-color: #ddd;
padding: 3px;
}
#top-1 {
position: absolute;
z-index: 10;
left: 8px;
top: 45px;
width: 330px;
height: 80px;
background-color: #888;
padding: 3px;
}
#relative {
position: relative;
z-index: 11;
background-color: #88a;
width: 330px;
height: 80px;
padding: 3px;
top: 30px;
left: 8px;
}
#top-2 {
position: absolute;
z-index: 10;
left: 0;
top: 0;
width: 400px;
height: 150px;
background-color: #dda;
padding: 3px;
}
<div class='wrapper'>
<div id="static">
I'm static, so behind #top-1, but have z-index higher than #top-1... Means z-index has no effect.
<br/>Text that is not visible, because behind #top-1 element
</div>
<div id='top-1'>
I'm above #static, because i have non-static position, so my z-index has effect.
</div>
</div>
<hr/>
<div class='wrapper'>
<div id="relative">
I'm relative and above #top-2, because my z-index higher than #top-2... Means z-index has taken effect.
</div>
<div id='top-2'>
I'm below #relative, because i have lover z-index.
<br/>Text that is not visible, because behind #top-1 element
</div>
</div>
z-index only works on positioned elements so position:absolute, position:relative or position:fixed
It does not behave like a z-index, because z-index specifies an ordering rule, but not the way how the element is displayed.
position: relative; says to go to the relative mode where it can compete the absolutely positioned elements.
Your problem here is that :before pseudo-element is a hierarchical sibling of span, and it takes the whole available parent width. So it fully covers a static span element.
When you make it relative, it becomes shown because when z-index is not specified for both non-static elements they are shown in the same order like they are placed in HTML (so element which is defined in HTML later is always on top).
Your structure is:
label
:before
span
so the span becomes visible.

Higher z-index does not change stacking order

What I am trying to do is, when the user hover on the image the image should reposition along the x-axis and it should reveal the .content. i have set z-index: 10 to image and z-index: 1 to .content to make .content to be underneath the image. but .content still remains on top of the image. Please help me..
Here is my code:
html
<div class="holder">
<img src="http://placehold.it/350x150" />
<div class="content">
<h3>hello there</h3>
view more
<div/>
</div>
css
.holder {
margin-top: 130px;
position: relative;
}
img {
display: block;
transition: -webkit-transform 0.5s;
transition: -moz-transform 0.5s;
z-index: 10;
}
.content {
background: blue;
height: 100%;
color: white;
z-index: 1;
position: absolute;
top: auto;
bottom: 0;
}
a {
color: white;
background: red;
padding: 10px;
}
.holder:hover img {
-webkit-transform: translateX(90px);
-moz-transform: translateX(90px);
}
Here I corrected issue of my code thanks to Jones G. Drange. As he pointed out in his comment
"z-index can only be modified in elements with a position other than static. Your img has position: static by default"
jsfiddle
img {
position: relative;
}