The title pretty much says it all but here is an example.
Let's say I have a CSS 'loading spinner' as below:
.spinner {
height: 50px;
width: 50px;
position: relative;
animation: rotate .6s infinite linear;
border-left: 6px solid #222;
border-right: 6px solid #222;
border-bottom: 6px solid #222;;
border-top: 6px solid #ccc;
border-radius: 100%;
}
#keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
I want to add a pseudo element to this - for example with content: 'loading...' before or after .spinner.
Is it possible to ensure the pseudo element does not inherit the animation from .spinner, or must a pseudo element always take what the parent has?
As the pseudo-element is a child element of the parent it will continue to get rotated as long as parent has the animation. Even setting animation: none on the pseudo element will have no effect.
The only way to make it look as though the child has no animation is to reverse the effect like shown in below snippet. What is being done is that the very same animation is added to the pseudo element but the animation-direction is set as reverse. This means that the pseudo get the exact reverse transform effect and thus would retain it in the same position.
.spinner {
height: 50px;
width: 50px;
position: relative;
animation: rotation .6s infinite linear;
border-left: 6px solid #222;
border-right: 6px solid #222;
border-bottom: 6px solid #222;
border-top: 6px solid #ccc;
border-radius: 100%;
}
.spinner:after {
position: absolute;
content: 'Loading..';
top: 0px;
left: 0px;
height: 100%;
width: 100%;
animation: rotation .6s infinite linear reverse; /* added this line */
}
#keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
<div class='spinner'></div>
The above snippet uses the default setting for transform-origin which is 50% 50% but if the child pseudo-element has padding and/or margin then the transform-origin setting has to be adjusted accordingly to avoid the pseudo-element from producing a shivering like effect. The calculation logic is provided in the below snippet.
.spinner {
height: 50px;
width: 50px;
position: relative;
animation: rotation .6s infinite linear;
border-left: 6px solid #222;
border-right: 6px solid #222;
border-bottom: 6px solid #222;
border-top: 6px solid #ccc;
border-radius: 100%;
}
.spinner.parent-padded-margin {
padding: 10px;
margin: 10px;
}
.spinner:after {
position: absolute;
content: 'Loading..';
top: 0px;
left: 0px;
height: 100%;
width: 100%;
animation: rotation .6s infinite linear reverse;
/* added this line */
}
.spinner.child-padded-margin:after {
padding: 10px 8px;
margin: 5px 4px;
transform-origin: calc(50% - 12px) calc(50% - 15px); /* calc(50% - ((padding-left + padding-right)/2 + margin-left)) calc(50% - ((padding-top + padding-bottom)/2 + margin-top)) */
}
.spinner.child-padded-margin-2:after {
padding: 10px 6px 16px 14px;
margin: 7px 12px 5px 10px;
transform-origin: calc(50% - 20px) calc(50% - 20px); /* calc(50% - ((padding-left + padding-right)/2 + margin-left)) calc(50% - ((padding-top + padding-bottom)/2 + margin-top)) */
}
#keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
<div class='spinner'></div>
<div class='spinner parent-padded-margin'></div>
<div class='spinner child-padded-margin'></div>
<div class='spinner child-padded-margin-2'></div>
Positioning the pseudo-element (using top,left,bottom,right ) also has affects the animation. It would also require the transform-origin to be modified accordinly in-order for the animation to work properly. A sample is available in the below snippet.
.spinner {
height: 50px;
width: 50px;
position: relative;
animation: rotation .6s infinite linear;
border-left: 6px solid #222;
border-right: 6px solid #222;
border-bottom: 6px solid #222;
border-top: 6px solid #ccc;
border-radius: 100%;
}
.spinner.parent-padded-margin {
padding: 10px;
margin: 10px;
}
.spinner:after {
position: absolute;
content: 'Loading..';
height: 100%;
width: 100%;
animation: rotation .6s infinite linear reverse; /* added this line */
}
.spinner.child-positioned{
margin-bottom: 40px;
}
.spinner.child-positioned:after {
top: 120%;
left: 2%;
transform-origin: calc(50% - 2%) calc(50% - 120%); /* basically need to subtract the distance from the left and top of the container */
}
.spinner.child-positioned-negative:after {
bottom: -120%;
right: -2%;
transform-origin: calc(50% - 2%) calc(50% - 120%); /* basically need to subtract the distance from the left and top of the container */
}
#keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
<div class='spinner child-positioned'></div>
<div class='spinner child-positioned-negative'></div>
Note: Both the above solutions work perfectly fine in latest versions of Chrome, Opera and Safari but are causing the text to have a slanted appearance in IE 11, Edge and Firefox. Firefox seems to require a separate animation which goes from rotate(-10deg) to rotate(-370deg) for FF while it gets more complex in IE.
The only alternate without setting the reverse animation on pseudo (child) element would be to make use of the method mentioned by Chris in his comment. That would mean setting borders and the animation directly to the pseudo element. This would mean that the parent's contents would remain unaffected as the parent won't get affected by a transform on the child.
.spinner {
height: 50px;
width: 50px;
position: relative;
}
.spinner:after {
position: absolute;
content: '';
top: 0px;
left: 0px;
height: 100%;
width: 100%;
animation: rotation .6s infinite linear;
border-left: 6px solid #222;
border-right: 6px solid #222;
border-bottom: 6px solid #222;
border-top: 6px solid #ccc;
border-radius: 100%;
}
#keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
<div class='spinner'>Loading...</div>
For completeness to the question alongside the comprehensive answer from #Harry I produced a version with the text below the spinner. The method of this is to use the .spinner as a canvas, put the actual spinning circle :before and the loading... in the :after as follows:
.spinner:before {
content: ' ';
display: block;
height: 50px;
width: 50px;
margin: 24px auto 6px auto;
animation: rotation .6s infinite linear;
border-left: 6px solid #222;
border-right: 6px solid #222;
border-bottom: 6px solid #222;;
border-top: 6px solid #ccc;
border-radius: 100%;
}
.spinner {
height: 100px;
width: 100px;
position: relative;
}
.spinner:after {
display: block;
text-align: center;
content: 'loading...';
}
#keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
<div class='spinner'></div>
Related
here is the shape i want to do enter link description here
P.S.I am still learning the front-end stuff so could you pls help me with this assignment.
Here is the HTML code <div>Elzero</div>
here is the CSS code i tried to do with
* {
box-sizing: border-box;
}
div {
width: 200px;
height: 200px;
background-color: #eee;
margin: 80px auto;
color: black;
font-size: 50px;
font-weight: bold;
text-align: center;
line-height: 200px;
border-radius: 50%;
}
::after {
content: "";
width: 200px;
height: 200px;
background-color: #03a9f4;
margin: 80px auto;
border-radius: 50%;
position: absolute;
transform: translate(-190px, -80px);
z-index: -1;
}
::before {
content: "";
width: 200px;
height: 200px;
background-color: #e91e63;
margin: 80px auto;
border-radius: 50%;
position: absolute;
top: 0px;
z-index: -2;
}
div:hover {
transition: all 0.5s;
transform: rotate(180deg);
}
As you are constrained to use just one div, this snippet builds on your idea of having the pseudo elements but creating them with conic-gradient backgrounds and the 'main' div having the light gray circular background created using a radial gradient. That way it creates these 3 shapes.
and overlays them to give the impression of 3/4 circles. It then uses CSS animation to rotate them on hover.
Obviously you will want to play with the dimensions, the animations timings and directions to get exactly what you want but this should give a start.
* {
box-sizing: border-box;
}
div {
width: 200px;
height: 200px;
background-image: radial-gradient(#eee 0 55%, transparent 55% 100%);
margin: 80px auto;
color: black;
font-size: 50px;
font-weight: bold;
text-align: center;
line-height: 200px;
border-radius: 50%;
position: relative;
}
div::after {
content: "";
width: 200px;
height: 200px;
border-radius: 50%;
position: absolute;
top: 0;
left: 0;
z-index: -2;
background-image: conic-gradient(#03a9f4 0deg 45deg, white 45deg 135deg, #03a9f4 135deg 360deg);
}
div::before {
content: "";
width: calc(100% - 10%);
height: calc(100% - 10%);
position: absolute;
border-radius: 50%;
position: absolute;
top: 5%;
left: 5%;
z-index: -1;
background-image: conic-gradient(#e91e63 0, #e91e63 225deg, white 225deg, white 315deg, #e91e63 315deg, #e91e63 360deg);
}
div:hover::after {
animation: rot .4s linear;
}
div:hover::before {
animation: rot .4s linear;
animation-delay: .1s;
animation-direction: reverse;
}
#keyframes rot {
0% {
transform: rotate(0);
}
25% {
transform: rotate(180deg);
}
50% {
transform: rotate(180deg);
}
75% {
transform: rotate(0);
}
100% {}
}
<div>Elzero
</div>
also here is example in less:
https://codepen.io/nikitahl/pen/XooBXd
if you want to use css here is a converter:
https://jsonformatter.org/less-to-css
I'm trying to create a loading DIV that has a border that looks like an indeterminate progress ring spinner.
I'm pretty close based on one of the examples on https://css-tricks.com/gradient-borders-in-css/
This is great when the border doesn't rotate. When you set the border in the :before element to match the transparent border in the gradient-box element then the static gradient border looks perfect.
However, once the animation is added, because the whole :before element rotates you get a pretty odd effect - as shown in the example below.
.gradient-box {
display: flex;
align-items: center;
width: 90%;
margin: auto;
max-width: 22em;
position: relative;
padding: 30% 2em;
box-sizing: border-box;
border: 5px solid blue;
color: #FFF;
background: #000;
background-clip: padding-box; /* !importanté */
border: solid 5px transparent; /* !importanté */
border-radius: 1em;
}
.gradient-box:before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -1;
margin: -35px; /* !importanté */
border-radius: inherit; /* !importanté */
background: conic-gradient(#0000ff00, #ff0000ff);
-webkit-animation: rotate-border 5s linear infinite;
-moz-animation: rotate-border 5s linear infinite;
-o-animation: rotate-border 5s linear infinite;
animation: rotate-border 3s linear infinite;
}
#keyframes rotate-border {
to {
transform: rotate(360deg);
}
}
html { height: 100%; background: #000; display: flex; }
body { margin: auto; }
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Loading DIV Test</title>
</head>
<body>
<div id="loadingBox" class="gradient-box">
<p>Loading.</p>
</div>
</body>
I've tried playing about with overflow: hidden; but the border just disappears.. is there any way to 'mask' the :before element in a way that whatever is behind this loading Div is still visible behind it and so that the border stays as its intended width?
Basically, my goal is that the colour gradient in the border rotates to give the effect of a spinning/rotating edge.
I like your original idea with using overflow: hidden, but to make it work I had to include an extra wrapper div.
The outer wrapper defines a padding which serves as the display area for the gradient border
The inner div is just the content box with a black background
.loading-box-container {
--size: 200px;
--radius: 10px;
position: relative;
width: var(--size);
height: var(--size);
padding: var(--radius);
border-radius: var(--radius);
overflow: hidden;
}
.loading-box {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
background: #000;
border-radius: var(--radius);
}
.loading-box-container::before {
content: '';
width: 150%; /* The upscaling allows the box to fill its container even when rotated */
height: 150%;
position: absolute;
top: -25%; left: -25%;
background: conic-gradient(#0000ff00, #ff0000ff);
animation: rotate-border 5s linear infinite;
}
#keyframes rotate-border {
to {
transform: rotate(360deg);
}
}
<div class="loading-box-container">
<div class="loading-box">
<p>Loading</p>
</div>
</div>
An alternative: Using #property
There's a much more elegant solution using #property, but unfortunately it only works on Chrome. I'm including here in case one day it becomes more universally supported or support for other browsers isn't important for your use case.
The conic-gradient function has a parameter that allows you to specify at what angle the gradient starts. If we can animate just that parameter, perhaps using a CSS variable, then we can animate the border with just a single div and without actually rotating anything.
Unfortunately, without some hinting the browser doesn't know how to transition a CSS variable. Therefore, we use #property to indicate the variable is an angle, telling the browser how to transition it.
#property --rotation {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.loading-box {
--size: 200px;
--radius: 10px;
position: relative;
width: var(--size);
height: var(--size);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
background: #000;
border-radius: var(--radius);
margin: var(--radius);
}
.loading-box::before {
--rotation: 0deg;
content: '';
width: calc(100% + 2 * var(--radius));
height: calc(100% + 2 * var(--radius));
border-radius: var(--radius);
position: absolute;
top: calc(-1 * var(--radius)); left: calc(-1 * var(--radius));
background: conic-gradient(from var(--rotation), #0000ff00, #ff0000ff);
animation: rotate-border 5s linear infinite;
z-index: -1;
}
#keyframes rotate-border {
to {
--rotation: 360deg;
}
}
<div class="loading-box">
<p>Loading</p>
</div>
CanIUse for #property indicates this will only work in Chrome and Edge as of this post.
Hi is this what you are looking for?
What I did was I added a new div which will be the "mask" as well as a container div for both the mask and the loadingBox.
I then sized the mask to be a little larger than your visible area, make it a transparent background, and then gave it a large outline the same color as your background to effectively mask out a border. I then fiddled with z-indexs of the mask, the loadingbox and the before. I also added some actual borders on mask to box it out into a nice shape.
Take a look:
.gradient-box {
display: flex;
align-items: center;
width: 90%;
margin: auto;
max-width: 22em;
position: relative;
padding: 30% 2em;
box-sizing: border-box;
border: 5px solid blue;
color: #FFF;
background: #000;
background-clip: padding-box; /* !importanté */
border: solid 5px transparent; /* !importanté */
border-radius: 1em;
}
.gradient-box:before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -3;
margin: -35px; /* !importanté */
border-radius: inherit; /* !importanté */
background: conic-gradient(#0000ff00, #ff0000ff);
-webkit-animation: rotate-border 5s linear infinite;
-moz-animation: rotate-border 5s linear infinite;
-o-animation: rotate-border 5s linear infinite;
animation: rotate-border 3s linear infinite;
}
#keyframes rotate-border {
to {
transform: rotate(360deg);
}
}
html { height: 100%; background: #000; display: flex; }
body { margin: auto; }
.mask {
position: absolute;
box-sizing: border-box;
background-color: transparent;
outline: 65px solid black;
height: 100%;
width: 100%;
border: 2px solid black;
border-left: 7px solid black;
border-right: 7px solid black;
z-index: -1;
}
.container {
position: relative;
}
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Loading DIV Test</title>
</head>
<body>
<div class="container">
<div class="mask"></div>
<div id="loadingBox" class="gradient-box">
<p>Loading.</p>
</div>
</div>
</body>
how to reduce the width of div from both left and right at the same time when i mouse over the div?
something like this -> example
i don't know why the bottom div is just reduced from left.
#border-top-category {
position: absolute;
border-top: solid 20px rgb(234, 198, 255);
width: 100px;
top: 0px;
right: 10px;
transition: width 2s;
}
#border-top-category:hover {
width: 50px;
}
<div id="border-top-category"></div>
Instead of animating the width, you could change the scale of the element on hover.
Set the default scaleX to 1 and change it on hover to 0.
Your CSS code could then look like this:
#border-top-category {
position: absolute;
border-top: solid 20px rgb(234, 198, 255);
width: 100px;
transform: scaleX(1);
transform-origin: center;
top: 0px;
right: 10px;
transition: transform 2s;
}
#border-top-category:hover {
transform: scaleX(0);
}
<div id="border-top-category"></div>
#border-top-category {
position: absolute;
border-top: solid 20px rgb(234, 198, 255);
width: 100px;
top: 0px;
right: 10px;
transition: transform 2s;
}
#border-top-category:hover {
transform: scaleX(0.5);
}
Use transition to transform the X axis (scaleX(k) it will make the x axis to k time)
This question already has answers here:
Position absolute but relative to parent
(5 answers)
Closed 2 years ago.
here is my attempt to have overlay animation effect but overlay is not hiding after getting out of the underlying div- 'overflowTest'
#overflowTest {
background: #ff0000;
color: white;
padding: 15px;
width: 150px;
height: 100px;
overflow: hidden;
text-shadow: 6px 6px 5px black;
}
#box{
position: absolute;
width: 60px;
height: 60px;
border: 5px solid black;
animation-name: go;
animation-duration: 6s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
transform: rotate(45deg);
}
#keyframes go {
0%{
border: 3px solid red;
}
100%{
border: 3px solid red;
transform: translateX(200px);
}
}
<div id="overflowTest"><div id="box"></div><div id="hel">This is demo text to test overlay animation on it</div></div>
please help in hiding the overlay when it is outside the 'overflowTest' div
You need to add position: relative; to the parent #overflowTest.
An Element with position: absolute; will behave absolute to the first parent with a relative position. If no other is declared as relative, it will behave absolute to the page itself, which was happening before.
#overflowTest {
position: relative;
background: #ff0000;
color: white;
padding: 15px;
width: 150px;
height: 100px;
overflow: hidden;
text-shadow: 6px 6px 5px black;
}
#box{
position: absolute;
width: 60px;
height: 60px;
border: 5px solid black;
animation-name: go;
animation-duration: 6s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
transform: rotate(45deg);
}
#keyframes go {
0%{
border: 3px solid #ffaaaa;
}
100%{
border: 3px solid #ffaaaa;
transform: translateX(200px);
}
}
<div id="overflowTest">
<div id="box"></div>
<div id="hel">This is demo text to test overlay animation on it</div>
</div>
Hope that is what you are looking for!
The property you should use is z-index.
Setting z-index:-1; will help you get the desired result. z-index works only when position property is also added.
Try this,
#overflowTest {
background: #ff0000;
color: white;
padding: 15px;
width: 150px;
height: 100px;
overflow: hidden;
text-shadow: 6px 6px 5px black;
}
#box{
position: absolute;
width: 60px;
height: 60px;
border: 5px solid black;
animation-name: go;
animation-duration: 6s;
animation-fill-mode: forwards;
animation-iteration-count: infinite;
transform: rotate(45deg);
z-index:-1;
}
#keyframes go {
0%{
border: 3px solid red;
}
100%{
border: 3px solid red;
transform: translateX(200px);
}
}
<div id="overflowTest"><div id="box"></div><div id="hel">This is demo text to test overlay animation on it</div></div>
Hope it helps.!! Happy Coding!!
I first tried implementing it through two triangles. And got a satisfactory output
#wrapper {
margin-left: 40vw;
margin-top: 20vh;
}
#fidgetu {
width: 0;
height: 0;
position: absolute;
margin-top: 3vh;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid red;
animation: rotate 2s linear infinite;
}
#fidgetd {
width: 0;
height: 0;
position: absolute;
margin-top: 3vh;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-top: 100px solid red;
animation: rotate 2s linear infinite;
}
#keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<div id="wrapper">
<div id="fidgetu">
</div>
<div id="fidgetd">
</div>
</div>
I guess drawing a fidget spinner would require 4 div circles and 3 div rectangles to connect the central circle to the other three and a wrapper div (applying animate property to this div). But the positioning is messing up.
Now how do I position them appropriately such that the entire block rotates around its center?
Set an element as the base spinner, and then 3 childs of this one as the outer circles.
if the outer ones are positioned over the first one, just rotating the base elements will handle the rotation of the others.
A litlle tricky are the curves connecting the inner and the outer. I have set a solution, but there is some missalignment. It still needs a last adjustment on the pixel values (but it's hard to get it exactly)
.spinner, .outer {
width: 100px;
height: 100px;
border-radius: 50%;
position: absolute;
transform-style: preserve-3d;
}
.spinner {
background-color: teal;
border: solid 20px tomato;
margin: 100px;
animation: rotate 4s infinite linear;
}
.outer {
background-color: lightblue;
border: solid 20px blue;
left: -20px;
top: -20px;
}
.outer:before {
content: "";
position: absolute;
width: 150px;
height: 150px;
border-radius: 50%;
transform: translate(-91px, 104px);
box-shadow: 0px -55px 0px -33px blue;
}
.outer:after {
content: "";
position: absolute;
width: 150px;
height: 150px;
border-radius: 50%;
transform: translate(-83px, -156px);
box-shadow: 0px 55px 0px -33px blue;
}
.outer:nth-child(1) {
transform: translate3D(120px, 0px, -10px);
}
.outer:nth-child(2) {
transform: rotate(120deg) translate3D(120px, 0px, -10px);
}
.outer:nth-child(3) {
transform: rotate(240deg) translate3D(120px, 0px, -10px);
}
#keyframes rotate {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
<div class="spinner">
<div class="outer"></div>
<div class="outer"></div>
<div class="outer"></div>
</div>