CSS - onhover pulls down div vertically when page width is small - css

I am working on a site where there is an info icon next to the logo in the center of the page. When hovering over the info icon, a text box displays. For some reason, when the screen width is shorter than around 1300px, the entire logo as well as the info box is pulled down vertically. Not sure what aspect of onhover could be causing this.
I tried to manually move down the info icon with media queries but that is not a good way of doing it.
Here is react component:
function HomeContent() {
return (
<>
<div className="center-div">
<img src={tenGreenLogo} className="ten-green-logo" alt=""></img>
<div className="info-div">
<img src={infoIcon} alt="" className="info-icon"></img>
<p className="hidden-info">
The 10Green Score indicates the health of your environment using a
number from 0 to 10. The higher the score, the healthier your
environment.
<br></br>
<br></br>
Factors that can reduce your score include unhealthy air quality
readings and poor environmental monitoring.
</p>
</div>
</div>
<Globe />
</>
);
}
export default HomeContent;
and here is CSS for that component:
.center-div {
display: flex;
justify-content: center;
align-items: center;
padding-top: 7rem;
padding-bottom: 0rem;
scroll-behavior: smooth;
}
.ten-green-logo {
max-width: 40%;
animation: transitionIn 1s;
padding-right: 1rem;
}
.info-div {
width: 2rem;
margin-top: auto;
margin-bottom: 0;
}
.info-icon {
width: 2rem;
animation: transitionIn 1s;
}
.hidden-info {
display: none;
}
.info-icon:hover + .hidden-info {
display: block;
position: relative;
background-color: white;
padding: 0.6rem;
border-radius: 5%;
width: 20rem;
font-size: 80%;
right: 7.5rem;
top: 10.5rem;
}
.info-icon:hover {
position: relative;
top: 10.6rem;
}
#keyframes transitionRight {
from {
transform: translateX(0rem);
}
to {
transform: translateX(2rem);
}
}
#keyframes transitionIn {
from {
opacity: 0;
transform: translateY(4rem);
}
to {
opacity: 1;
transform: translateY(0rem);
}
}
Any help would be greatly appreciated.

use this style, you have to use position absolute on hidden-info
.info-div {
position: relative;
}
.hidden-info {
display: none;
position: absolute;
background-color: white;
padding: 0.6rem;
border-radius: 5%;
width: 20rem;
font-size: 80%;
right: 7.5rem;
top: 10.5rem;
}
.info-icon:hover + .hidden-info {
display: block;
}

Related

How to style <details> and <summary>?

Just found out about and tags. Default gives out a very crude style. I had success using the ::marker Pseudo Element to remove the default marker, but don't know how to put it on the right side. Used the ::after Pseudo Element but can't animate it (Rotate 180deg or scale it) when the summary "opens". Is there a proper way to do it? Or did I miss anything with my method? Thanks.
PS: Since I am a newb, I don't know how to get the Google icon font to the codepen. However, you will see what I tried to do with the expand_arrow icon.
* {
box-sizing: border-box;
margin: 0px;
padding: 0px;
}
.thing {
background-color: cadetblue;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.new_game {
font-size: 3rem;
}
.what_is_question {
background-color: cornflowerblue;
cursor: pointer;
}
.what_is_question[open] {
background-color: darkmagenta;
}
.what_is_question {
font-size: 5rem;
}
.question_title {
position: relative;
}
.question_title::after {
content: url(./images/expand_more_black_24dp.svg);
}
.what_is_question[open] .question_title::after {
transform: rotate(180deg);
}
.question_title::marker {
content: none;
}
.answer {
background-color: darkkhaki;
font-size: 3rem;
padding-left: 3.5%;
}
<div class="thing">
<h1 class="new_game">QnA</h1>
<details class="what_is_question">
<summary class="question_title">What is the question?</summary>
<p class="answer">The question is the answer.</p>
</details>
</div>
https://codepen.io/consig1iere/pen/bGWXRMW
The problem is that you can't apply transforms to elements with display: inline.
So add display: inline-block; to your .question-title
.what_is_question[open] .question_title::after {
display: inline-block;
transform: rotate(180deg);
}

Lazy Load Iframe Only After Modal is Triggered

Issue: I cannot natively lazy load an iframe on a modal window. When I check the waterfall in Inspect element > Network, the iframe loads immediately.
Goal: The iframe should load ONLY when modal is triggered. Can you help please?
I don't use dependencies like jQuery, but javascript is OK if it provides a solution. I tried the native and several other lazy loading solutions with no success.
My code:
.modal-state {
display: none
}
.modal-state:checked+.modal {
opacity: 1;
visibility: visible
}
.modal-state:checked+.modal .modal__inner {
top: 0
}
.modal {
opacity: 0;
visibility: hidden;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
text-align: left;
background: #f2f2f2;
transition: opacity .01s ease;
z-index: 7;
display: flex;
flex-direction: column;
height: 100vh;
overflow-y: auto
}
.modal__bg {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
cursor: pointer
}
.modal__inner {
transition: top .01s ease;
height: max-content;
position: relative;
max-width: 1200px;
width: -webkit-fill-available;
margin: auto;
padding: 1em 1em;
}
.modal__close {
position: absolute;
right: 1.1em;
top: 0;
/*-.3em*/
width: 1.1em;
height: 1.1em;
cursor: pointer;
z-index: 1
}
.modal__close:after,
.modal__close:before {
content: '';
position: absolute;
width: 2px;
height: 1.5em;
background: #999;
display: block;
transform: rotate(45deg);
left: 50%;
margin: -3px 0 0 -1px;
top: 0
}
.modal__close:hover:after,
.modal__close:hover:before {
background: #000
}
.modal__close:before {
transform: rotate(-45deg)
}
.container-pay {
position: relative;
width: 100%;
height: 200px;
overflow: hidden;
padding-top: 56.25%;
/* 16:9 Aspect Ratio */
}
.responsive-iframe-pay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
border: none;
}
<p>In our <label for="modal-store">merchandize store</label>.</p>
<input class="modal-state" id="modal-store" type="checkbox" />
<div class="modal">
<label class="modal__bg" for="modal-store"></label>
<div class="modal__inner"><label class="modal__close" for="modal-store"></label>
<p>
<div class="container-pay">
<iframe loading="lazy" class="responsive-iframe-pay" src="https://store.website.com"></iframe>
</div>
</p>
</div>
</div>
iFrames load when they're encountered in the rendered HTML DOM. Since your iFrame exists as part of the initially loaded code, it will load when the parser hits that portion of the HTML.
You can defeat that initial load action by either adding the iFrame to the DOM or modifying the URL right before the modal window is opened.
Modifying the <iframe src="" /> is likely the best solution since it will keep the loaded content for any additional times the modal is displayed.
You can do this by adding an "onchange" event to the checkbox which will run the javascript to change the src attribute of the iframe.
Make sure to change the src on the iframe to an empty string"" so it doesn't try to load anything right away.
var toggle = document.getElementById('modal-store');
var frame = document.getElementById('the-iframe');
var urlTarg = "https://google.com";
// put the page you want to load in the frame in the urlTarg
function toggleModal() {
if(toggle.checked && frame.src != urlTarg){
frame.src = urlTarg;
}
}
.modal-state {
display: none
}
.modal-state:checked+.modal {
opacity: 1;
visibility: visible
}
.modal-state:checked+.modal .modal__inner {
top: 0
}
.modal {
opacity: 0;
visibility: hidden;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
text-align: left;
background: #f2f2f2;
transition: opacity .01s ease;
z-index: 7;
display: flex;
flex-direction: column;
height: 100vh;
overflow-y: auto
}
.modal__bg {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
cursor: pointer
}
.modal__inner {
transition: top .01s ease;
height: max-content;
position: relative;
max-width: 1200px;
width: -webkit-fill-available;
margin: auto;
padding: 1em 1em;
}
.modal__close {
position: absolute;
right: 1.1em;
top: 0;
/*-.3em*/
width: 1.1em;
height: 1.1em;
cursor: pointer;
z-index: 1
}
.modal__close:after,
.modal__close:before {
content: '';
position: absolute;
width: 2px;
height: 1.5em;
background: #999;
display: block;
transform: rotate(45deg);
left: 50%;
margin: -3px 0 0 -1px;
top: 0
}
.modal__close:hover:after,
.modal__close:hover:before {
background: #000
}
.modal__close:before {
transform: rotate(-45deg)
}
.container-pay {
position: relative;
width: 100%;
height: 200px;
overflow: hidden;
padding-top: 56.25%;
/* 16:9 Aspect Ratio */
}
.responsive-iframe-pay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
border: none;
}
<p>In our <label for="modal-store">merchandize store</label>.</p>
<input class="modal-state" id="modal-store" type="checkbox" onchange="toggleModal" />
<div class="modal">
<label class="modal__bg" for="modal-store"></label>
<div class="modal__inner"><label class="modal__close" for="modal-store"></label>
<p>
<div class="container-pay">
<iframe id="the-iframe" loading="lazy" class="responsive-iframe-pay" src=""></iframe>
</div>
</p>
</div>
</div>
Edit: Apply to multiple different buttons showing content in the same iframe
You can apply this to multiple iframe targets on the same page or the same site with a few modifications. This assumes that:
You'll re-use the modal window AND iframe HTML code.
That you want to display a different URL each time the modal is opened
The modal window HTML exists on each HTML page that you want to have modal + iframe.
You would modify the Javascript to something like this:
// you'll pass all the required values to the function
function toggleModal(checkbox, frameTarg, urlTarg) {
var frame = document.getElementById(frameTarg);
if(checkbox.checked && frame.src != urlTarg){
frame.src = urlTarg;
}
}
You will only need the javascript once per parent page since the same function can work for any combo of checkboxes, iframes, and URLs
And the checkbox HTML to:
(Note: the same function could be applied to a button, link, etc)
<input class="modal-state"
id="modal-store"
type="checkbox"
onchange="toggleModal(this, 'the-iframe', 'https://theurltoloadinframe.com')"
/>
<input class="modal-state2"
id="modal-store"
type="checkbox"
onchange="toggleModal(this, 'iframe2', 'https://someotherurltoload.com')"
/>
Basically, the function expects you to pass in:
The current checkbox - (denoted by this)
The ID of the iframe you want to change make sure its a string with quotes
The URL you want to load in the iFrame also in a string

Long heading text results in empty gap

Currently my movie heading code looks like this:
.moview-info h1 {
color: #fff;
margin: 0 0 15px 0;
font-weight: normal;}
And I tried adding these lines:
display:block;
word-wrap: break-word;
max-width: 700px;
It fixes the issue partly, because when I'm shrinking my browser window, the star rating causes the heading go up and not overlap. Is there any way to fix this issue besides not displaying star rating?
Rating star code:
position: absolute;
left: 50%;
margin-left: -70px;
font-size: 130px;
width: 140px;
height: 140px;
line-height: 140px;
short title long title
This styling is usually done with { display: flex; justify-content: space-between; } as the CSS of the parent element of the title and the star. Demo below (the JS is just to animate the text):
const titleElt = document.querySelector('#title');
let titleValue = '';
setInterval(() => {
if (titleValue.length>70) {
titleValue = '';
} else {
titleValue += Math.floor(10*Math.random());
}
titleElt.innerHTML = titleValue;
}, 100);
body {
margin: 0;
font-size: 2em;
}
header {
display: flex;
justify-content: space-between;
width: 100%;
}
#title {
width: 90%;
word-wrap: break-word;
}
#star {
width: 10%;
}
<header>
<div id="title"></div>
<div id="star">⭐</div>
</header>

Transition from sentence to one word

What I am trying to achieve is to animate an element width few words in the way that hovered word leaves in the center of the element and the rest smoothly goes out of the bounds. I would also keep it as clear as possible in HTML and not to use fixed pixel amount of margins/widths to position elements.
The very lousy sketch of what I have on my mind is here:
* {
transition: all 1.5s;
}
div {
min-width: 150px;
width: 30%;
height: 25px;
line-height: 25px;
background: orange;
text-align: center;
overflow: hidden;
}
div:hover {
background: white;
word-spacing: 300px;
}
a:hover::after,
a:hover::before {
content: ' ';
}
<div>
<a class="afirst" href="">First</a> & <a class="asecond" href="">Second</a>
</div>
Each word on hover should go to center (possibly not with those 'jumps' visible now when other words disapear). Do you have any ideas? I'm pretty sure that the way I try to follow with word-spacing is wrong.
The issue is that when increasing word spacing the text goes to new line and create this jump thing. So you may add white-space: nowrap and you can also use padding-left to push text and make it in the center :
div {
min-width: 150px;
width: 180px;
height: 25px;
line-height: 25px;
background: orange;
text-align: center;
overflow: hidden;
transition: all 0.5s;
box-sizing: border-box;
white-space: nowrap;
}
div:hover {
word-spacing: 80px;
padding-left: 80px;
background: white;
}
<div>
<a class="afirst" href="">First</a> & <a class="asecond" href="">Second</a>
</div>
Actually the word spacing is applied to the div so you cannot apply hover on word. It's also easier to apply this technique on the first word as the second one will be hide with overflow, but am not sure how you can do the same with the second word with the use of word-spacing.
Here is another idea on how you can do without word-spacing. I used some padding animation and also pseudo element to hide the first word when hovering the second one.
div {
min-width: 150px;
width: 180px;
height: 25px;
line-height: 25px;
background: orange;
text-align: center;
overflow: hidden;
transition: all 0.5s;
box-sizing: border-box;
white-space: nowrap;
}
.afirst,
.asecond {
position:relative;
display: inline-block;
transition: all 0.5s;
}
.afirst:hover {
padding: 0 44%;
background: white;
}
.asecond:hover {
padding: 0 50% 0 0;
background: white;
}
.asecond:hover::before {
content:" ";
position:absolute;
left:-50%;
width:50%;
top:0;
bottom:0;
z-index:99;
background:#fff;
}
<div>
<a class="afirst" href="">First</a> & <a class="asecond" href="">Second</a>
</div>
I think you can generalize this solution by using :before element in the left and :after element in the right to hide everything else.
Here is an example with multiple word (but not giving center alignement correctly, still need improvement) :
div {
min-width: 150px;
height: 25px;
line-height: 25px;
background: orange;
text-align: center;
overflow: hidden;
transition: all 0.5s;
box-sizing: border-box;
white-space: nowrap;
}
.word {
position: relative;
display: inline-block;
transition: all 0.5s;
}
.word:hover {
background: white;
padding: 0 40%;
}
.word:hover::before {
content: " ";
position: absolute;
left: -50%;
width: 50%;
top: 0;
bottom: 0;
z-index: 99;
background: #fff;
}
.word:hover::after {
content: " ";
position: absolute;
right: -50%;
width: 50%;
top: 0;
bottom: 0;
z-index: 99;
background: #fff;
}
<div>
<a class="word" href="">First</a> &
<a class="word" href="">Second</a> &
<a class="word" href="">third</a> &
<a class="word" href="">Fourth</a>
</div>
Another solution with perfect centering but less animation for the other words :
div {
position:relative;
height: 25px;
width:500px;
margin:0 auto;
line-height: 25px;
background: orange;
text-align: center;
overflow: hidden;
transition: all 0.5s;
box-sizing: border-box;
white-space: nowrap;
}
.word {
position: relative;
z-index:9;
display: inline-block;
transition: all 0.5s;
}
.word:hover {
position:absolute;
right:0;
left:0;
top:0;
bottom:0;
text-align:center;
background: white;
z-index:99;
}
<div>
<a class="word" href="">First</a> &
<a class="word" href="">Second</a> &
<a class="word" href="">third</a> &
<a class="word" href="">Fourth</a>
</div>
This version uses Flex Box, accommodates multiple items, and does not use fixed widths.
The trouble I kept having playing around with this was centering items without using fixed widths. Especially those that were later in line and trying to push items to the left.
Solution: flex box order.
You can set the order of flex items numerically, so simply setting it to -1 on hover puts the hovered item first in the list so you eliminate the issue of flexing items leftward.
The animation may not be as fluid as you're looking for because you can only transition order by integer. So it's immediately set along with the width, and then the other properties transition.
div.outer {
width: 50vw;
height: 30px;
background: orange;
text-align: center;
overflow: hidden;
position: relative;
padding: 0;
}
div.inner {
position: relative;
margin: 0 auto;
height: 30px;
width: 50vw;
display: flex;
justify-content: center;
align-items: center;
background: transparent;
flex-wrap: wrap;
}
span {
order: 1;
padding: 0 3px;
flex-shrink:1;
}
a {
z-index: 0;
order: 1;
height: 30px;
line-height: 30px;
flex-grow: 0;
flex-shrink: 1;
transition: background .5s, flex-grow .25s, flex-shrink .25s;
}
a:hover {
z-index: 10;
width: 50vw;
order: -1;
flex-grow: 1;
background: #eee;
}
<div class="outer">
<div class="inner">
First
<span>&</span>
Second
<span>&</span>
Third
</div>
</div>
This isn't perfect, but I think it comes pretty close to achieving what you were looking for. Using a little bit of jQuery was necessary for math (but not the animations themselves).
Hope this gets you further!
function reset() {
// reset all spans
$('.animate span').css('transform', '');
$('.animate span.active').removeClass('active');
}
$('.animate').on('mouseover', 'span:not(.active)', function() {
reset();
// set current hovered word to active
$(this).addClass('active');
// slide over previous/next words
$(this).prevAll().css('transform', 'translateX(-100vw)');
$(this).nextAll().css('transform', 'translateX(100vw)');
// determine offset for center of hovered word
var center = (window.innerWidth / 2) - ($(this).offset().left + ($(this).width() / 2));
// slide current word to center
$(this).css('transform', 'translateX(' + center + 'px)');
});
// when leaving animate section, reset
$('.animate').mouseleave(reset);
.animate {
background-color: orange;
color: white;
font-family: sans-serif;
text-align: center;
transition: .5s background-color, .5s color;
}
.animate:hover {
background-color: white;
color: orange;
}
span {
display: inline-block;
transition: 3s transform;
}
a {
color: inherit;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="animate">
<span>First</span> <span>&</span> <span>Second</span> <span>&</span> <span>Third</span> <span>and</span> <span>Fourth</span>
</div>

How to bottom align a button in bootstrap 4?

My question is actually more complex then the title, but I couldn't come up with a better one.
Initial Setup:
I use Bootstrap v4.0.0-alpha.2 and I ripped out this simple sidebar. I'm not sure why and if it's relevant but I also set flex: true in my _library-variable-overrides.scss (I use css-burrito) but since I only set it to try it out, I'm probably okay with turning it off. ;-)
What I want to do:
I would like to have a button in the sidebar that is bottom aligned. Ideally it's centered horizontally in the sidebar and has about 1em margin to the bottom.
What my code looks like:
_shell.scss & _sidenav.scss:
#shell-wrapper {
padding-left: 0;
transition: all 0.5s ease;
}
#shell-wrapper.toggled {
padding-left: 250px;
#shell-content-wrapper {
position: absolute;
margin-right: -250px;
}
}
#media(min-width:768px) {
#shell-wrapper {
padding-left: 250px;
}
#shell-wrapper.toggled {
padding-left: 0;
#shell-content-wrapper {
position: relative;
margin-right: 0;
}
}
#shell-content-wrapper {
padding: 20px;
position: relative;
}
}
#sidenav-wrapper {
z-index: 1000;
position: fixed;
left: 250px;
width: 0;
height: 100%;
margin-left: -250px;
overflow-y: auto;
background: #000;
transition: all 0.5s ease;
}
#shell-wrapper.toggled {
#sidenav-wrapper {
width: 250px;
}
}
#shell-content-wrapper {
width: 100%;
position: absolute;
padding: 15px;
}
/* Sidenav Styles */
.sidenav-nav {
position: absolute;
top: 0;
width: 250px;
margin: 0;
padding: 0;
list-style: none;
li {
text-indent: 20px;
line-height: 40px;
a {
display: block;
text-decoration: none;
color: #999999;
}
a:hover {
text-decoration: none;
color: #fff;
background: rgba(255, 255, 255, 0.2);
}
a:active, a:focus {
text-decoration: none;
}
}
>.sidenav-brand {
height: 65px;
font-size: 18px;
line-height: 60px;
a {
color: #999999;
}
a:hover {
color: #fff;
background: none;
}
}
}
#media(min-width:768px) {
#sidenav-wrapper {
width: 250px;
}
#shell-wrapper.toggled #sidenav-wrapper {
width: 0;
}
}
and index.html:
<div id="shell-wrapper" class="toggled">
<div id="sidenav-wrapper">
<ul class="sidenav-nav">
<li class="sidenav-brand">
Brand
</li>
<li>
Item 1
</li>
<li>
Item 2
</li>
<li id="logout">
<button class="btn btn-danger-outline">Logout</button>
</li>
</ul>
</div>
<button class="navbar-toggler" type="button">
☰
</button>
<div id="shell-content-wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<!--Main Content Here-->
</div>
</div>
</div>
</div>
</div>
The logout button is one in question. I just tried doing it as a <li> of the sidenav-nav but I'm not tied to this setup.
What I have tried so far:
a lot!
What came closest to what I want was adding this:
.sidenav-nav {
height: 100%;
}
#logout {
position: absolute;
bottom: 1em;
}
It's pretty close to my goal on a desktop browser, but hitting that show me this on a phone button in chrome, the logout button is just gone.
i haven't worked with css-buritto, but you could look into giving the button a class or id and passing the position:relative argument you can then set a bottom: 1em and that should position the button at the bottom. alternativly you can also look into the other position elements like fixed that could also do the trick
like you mentioned a the end
#logout {
position: relative;
bottom: 1em;
}

Resources