Example use (what I want)
div::after {
content: var(--mouse-x) ' / ' var(--mouse-y);
}
Test case showing it NOT working:
CodePen: CSS Variables in Pseudo Element's "content:" Property (a test case) by Jase Smith
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX)
document.documentElement.style.setProperty('--mouse-y', e.clientY)
// output for explanation text
document.querySelector('.x').innerHTML = e.clientX
document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */
div::after {
content: var(--mouse-x, 245)" / " var(--mouse-y, 327);
}
/* setup and presentation styles */
div::before {
content: 'mouse position:';
}
div {
position: absolute;
top: 0;
left: 0;
transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
width: 10em;
height: 10em;
background: #ff3b80;
color: #fff;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 100%;
will-change: transform;
}
body {
margin: 2em;
font-family: sans-serif;
}
p {
max-width: 50%;
min-width: 25em;
}
<!-- test case: element with pseudo element -->
<div></div>
<!-- explanation (not test case) -->
<main>
<pre><code>div::after {
content: var(--mouse-x) ' / ' var(--mouse-y);
}</code></pre>
<h1>If this worked...</h1>
<p>
We should see something like this: <b><span class="x">245</span> / <span class="y">327</span></b> updating with the mousemove coordinates inside the pseudo <i>::after</i> element for the div.
</p>
</main>
Edit for clarity: CSS custom properties with integer values can be displayed in a pseudo-element's content property via a CSS counter.
div {
--variable: 123;
}
span:after {
counter-reset: variable var(--variable);
content: counter(variable);
}
<div>The variable is <span></span>.</div>
.coordinates:before {
counter-reset: x var(--x) y var(--y);
content: 'The coordinates are (' counter(x) ', ' counter(y) ').';
}
<div class="coordinates" style="--x: 1; --y: 2"></div>
Original Answer
Got it to work using a hack involving CSS Counters. Enjoy.
div::after {
counter-reset: mouse-x var(--mouse-x, 245) mouse-y var(--mouse-y, 245);
content: counter(mouse-x) " / " counter(mouse-y);
}
Full code in action:
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX)
document.documentElement.style.setProperty('--mouse-y', e.clientY)
// output for explanation text
document.querySelector('.x').innerHTML = e.clientX
document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */
div::after {
counter-reset: mouse-x var(--mouse-x, 245) mouse-y var(--mouse-y, 245);
content: counter(mouse-x) " / " counter(mouse-y);
}
/* setup and presentation styles */
div::before {
content: 'mouse position:';
}
div {
position: absolute;
top: 0;
left: 0;
transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
width: 10em;
height: 10em;
background: #ff3b80;
color: #fff;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 100%;
will-change: transform;
}
body {
margin: 2em;
font-family: sans-serif;
}
p {
max-width: 50%;
min-width: 25em;
}
<!-- test case: element with pseudo element -->
<div></div>
<!-- explanation (not test case) -->
<main>
<pre><code>div::after {
content: var(--mouse-x) ' / ' var(--mouse-y);
}</code></pre>
<h1>If this worked...</h1>
<p>
We should see something like this: <b><span class="x">245</span> / <span class="y">327</span></b> updating with the mousemove coordinates inside the pseudo <i>::after</i> element for the div.
</p>
</main>
I'm not quite sure if I understood your question correctly, but I think here's a solution...
You can define a custom attribute to your <div> element.
<div data-position></div>
Then assign the position in this attribute with javascript:
var position = e.clientX + " " + e.clientY
document.querySelector("div").setAttribute('data-position', position)
Finally use the attr() CSS function in the content property of your pseudoelement.
div::after {
content: attr(data-position);
}
And voila.
Code Snippet:
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', e.clientX)
document.documentElement.style.setProperty('--mouse-y', e.clientY)
var position = e.clientX + "/" + e.clientY
document.querySelector("div").setAttribute('data-position', position)
// output for explanation text
document.querySelector('.x').innerHTML = e.clientX
document.querySelector('.y').innerHTML = e.clientY
})
/* what I want!! */
div::after {
content: attr(data-position);
}
/* setup and presentation styles */
div::before {
content: 'mouse position:';
}
div {
position: absolute;
top: 0;
left: 0;
transform: translate(calc(var(--mouse-x, 245) * 1px), calc(var(--mouse-y, 327) * 1px));
width: 10em;
height: 10em;
background: #ff3b80;
color: #fff;
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
border-radius: 100%;
will-change: transform;
}
body {
margin: 2em;
font-family: sans-serif;
}
p {
max-width: 50%;
min-width: 25em;
}
<div data-position></div>
<span class="x"></span>/<span class="y"></span>
content property only allows Strings, and since you are dealign with numbers and CSS cannot cast variables, you are left with the option to create another set of variables (from JS) which will serve as the printed values, and will be of type String.
To set --mouse-x-text as String, it's not enough to cast it to that type using the old casting trick 2+"" = "2", but JSON.stringify is the only way what I know that can output a "real" string, out of the already-string value, which kind-of mean a string of a string, since CSS seems to strip the first string-layer.
document.addEventListener('mousemove', ({clientX:x, clientY:y}) => {
const {style} = document.documentElement
style.setProperty('--mouse-x', x)
style.setProperty('--mouse-y', y)
// for printing
style.setProperty('--mouse-x-text', JSON.stringify(x+""))
style.setProperty('--mouse-y-text', JSON.stringify(y+""))
})
body::before{
content: "X:"var(--mouse-x-text)" Y:"var(--mouse-y-text);
}
You need quotation marks around the values of your custom properties.
document.documentElement.style.setProperty('--mouse-x', "'" + e.clientX + "'")
document.documentElement.style.setProperty('--mouse-y', "'" + e.clientY + "'")
Related
Edit: This bug has been fixed by Chromium some time ago
Simple Codepen: https://codepen.io/themanfromearth1/pen/WNRoyyW
With Slick Slider: https://codepen.io/maxbeat/pen/abNBrex
The error happens when you combine backdrop-filter blur with either transition-duration or transform in the parent.
Parent Div:
.slider {
transition-duration: 300ms; /** One of those two is enough **/
transition: transform 0.3s; /** But with either the bug occurs **/
transform: translate3d(-100px, 0, 0);
}
Child Div:
.slider__item {
background: rgba(206, 206, 206, 0.15);
backdrop-filter: blur(89px);
}
When you click the button, the CSS blur disappears for a second and then the slide is blurred again.
Chrome: Problem
Firefox: Works (you have to enable backdrop-filter first in about:config)
Webkit/Safari: Working
Edit: There is a confirmed bug-report on Chromium for this problem
https://bugs.chromium.org/p/chromium/issues/detail?id=1194050
Without using translate you can use positioning with left.
let translated = false
function translate3d() {
const div2 = document.getElementById("slider")
if (!translated) {
div2.style.left = '100px';
} else {
div2.style.left = '400px';
}
translated = !translated;
}
.slider {
display: flex;
align-items: center;
justify-content: center;
position: relative;
left: 400px;
/** Remove duration and blur works **/
transition-duration: 300ms;
/** Also disappears if you transform like this **/
/** transition: transform 0.3s **/
}
.slider__item {
height: 100px;
width: 100px;
background: rgba(206, 206, 206, 0.15);
backdrop-filter: blur(89px)!important;
margin: 6px;
margin-top: 200px;
}
.container {
height: 1900px;
width: 899px;
background-image:url(https://images.unsplash.com/photo-1616604745302-60a195c7061a?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1401&q=80);
}
.button {
position:absolute;
top: 350px;
left: 400px;
}
<div class="container">
<div class="slider" id="slider">
<div class="slider__item">1</div>
<div class="slider__item">2</div>
<div class="slider__item">3</div>
<div class="slider__item">4</div>
<div class="slider__item">5</div>
</div>
</div>
<div class="button">
<button type="button" onclick="translate3d()">Click to transform translate3d</button>
</div>
<div>
It's a bug !
I've encountered it myself while I was building my own portfolio.
The way I've solved it was by using an API for smooth scrolling that I wrote which is available here: https://github.com/CristianDavideConte/universalSmoothScroll
Basically the solution is: don't use translate and backdrop-filter:blur together, just make the parent container scrollable and smoothly scroll it whenever you need it.
I've rewrote your slider example so that it correctly works with the API:
const maxSlide = 5;
const minSlide = 1;
const visibleSlides = 3;
let numSlide = 0;
function init() {
let leftArrow = document.getElementById('left-arrow');
let rightArrow = document.getElementById('right-arrow');
let slider = document.getElementsByClassName('slider')[0];
let slides = slider.children;
leftArrow.onclick = (e) => {
e.preventDefault();
if(numSlide > minSlide - 1) {
numSlide--;
uss.scrollIntoView(slides[numSlide], true, null, null, true);
}
}
rightArrow.onclick = (e) => {
e.preventDefault();
if(numSlide < maxSlide - visibleSlides) {
numSlide++;
uss.scrollIntoView(slides[numSlide], true, null, null, true);
}
}
/* We apply an ease function to make it look pretty */
let whateverEaseFunctionYouLike = remaning => {return remaning / 15 + 1;};
uss.setXStepLengthCalculator(whateverEaseFunctionYouLike, slider);
}
* {
padding: 0;
margin: 0;
}
body {
height: 100vh;
overflow: hidden;
background: url(https://img.wallpapersafari.com/desktop/1366/768/66/57/D2uNEj.jpg) no-repeat; /* Moved from container */
background-size: cover; /* Moved from container */
}
.container {
width: 100vw; /* Added */
display: flex; /*Added */
height: 200px; /* Moved from slider__item */
/*height: 100%; Removed */
/*padding: 60px; Removed */
}
.slider {
width: 80%; /* Changed */
height: 100%; /* Added */
position: relative;
display:flex; /* Added */
overflow: hidden; /* Added */
/*margin-bottom: 60px; Removed*/
}
.slick-list {
overflow: hidden;
}
.slick-track {
display: flex;
}
.slider__item {
width: calc(74vw / 3); /* Added */
height: 80%; /* Added */
flex-shrink: 0; /* Added */
margin: auto 1vw; /* Changed */
display: flex;
align-items: center;
justify-content: center;
font: 24px arial;
background: rgba(255,255,255,0.5);
backdrop-filter: blur(10px);
}
.slick-dots {
display: flex;
justify-content: center;
position: absolute;
width: 100%;
}
.slick-dots li {
background: #fff;
width: 10px;
height: 10px;
margin: 5px;
border-radius: 50%;
cursor: pointer;
list-style:none
}
.slick-dots li.slick-active {
background: #000;
}
.slick-dots li button {
font-size: 0;
border: none;
opacity: 0;
}
.slick-arrow {
font-size: 0;
width: 30px;
height: 30px;
background: none;
border: none;
border-top: 4px solid #fff;
border-left: 4px solid #fff;
/*transform: rotate(-45deg); Moved */
/*position: absolute; Removed */
margin: auto; /* Added */
top: 100px;
cursor: pointer;
}
.slick-next {
transform: rotate(135deg);
right: -30px;
}
.slick-prev {
left: -30px;
}
#left-arrow {
transform: rotate(-45deg); /* Moved from slick-arrow*/
}
#right-arrow {
transform: rotate(135deg); /* Moved from slick-arrow and then changed */
}
<script src = "https://cdn.jsdelivr.net/npm/universalsmoothscroll#latest/universalsmoothscroll-min.js"></script> <!-- Added -->
<body onload="init()"> <!-- Added init -->
<div class="container">
<button id= "left-arrow" class ="slick-arrow"><</button> <!-- Added -->
<div class="slider">
<div class="slider__item">1</div>
<div class="slider__item">2</div>
<div class="slider__item">3</div>
<div class="slider__item">4</div>
<div class="slider__item">5</div>
</div>
<button id= "right-arrow" class ="slick-arrow">></button> <!-- Added -->
</div>
</body>
The advantage of this aproach is that you can customize the scrolling related part of your slider just as much as you did with "slick-slider".
If you need one more example of a carousel built with this API you can take a look at here: https://cristiandavideconte.github.io/myPersonalWebPage/#home
I would like my scroll to work like this when the user scrolls. e.g to start to fill up instead of moving.
Is it possible to make the scroll-thumb grow or to style the scrollbar-track-piece different before and after the thumb?
Here is a small example how to implement this loader
window.addEventListener("scroll", (e) => {
var html = document.documentElement;
let step = 100 / (html.scrollHeight - window.innerHeight);
let loader = document.getElementById("scrollprogress");
loader.style.width = (step * html.scrollTop) + "%";
})
#scrollprogress {
height: 5px;
position: fixed;
left: 0;
top: 0;
background: orange;
}
.backgr {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 5px;
background: lightgrey;
z-index: -1;
}
.box {
height: 3000px;
}
<div id="scrollprogress"></div>
<div class="backgr"></div>
<div class="box"></div>
You can approximate this using negative box shadow:
body::-webkit-scrollbar {
width: 1em;
}
body::-webkit-scrollbar-thumb {
background-color: orange;
box-shadow:-1000vmax -1000vmax 0px 1000vmax orange;
}
body {
width:300vw;
height:300vh;
background:linear-gradient(60deg,red,blue,orange);
margin:0;
}
html {
background:#fff;
}
I want an element to get applied some styles in a smooth way after 2 seconds. I tried setTimeout, to add the class after two seconds, but despite having declared transition: all 1s to absolutely everything, when the class (containing new styles )is added, I can't see any "transition", the new styles are just applied without smoothness.
How can I fix it?
var s2 = document.querySelector("#section2");
setTimeout(() => {
s2.classList.add("up");
}, 2000);
html * {
transition: 1s all;
}
body {
overflow: hidden;
}
#section1 {
background: steelblue;
}
#section2 {
background: indigo;
position: absolute;
}
section {
width: 100%;
height: 100vh;
}
.up {
top: 0px;
}
<section id="section1"></section>
<section id="section2"></section>
What I want to achieve is that the div that is below the first one start moving up until it reaches the top of the window, like a "slide-up" effect. Why are "transition" property being ignored after adding the class containing this new styles?
You need to apply an initial value for the top rule to allow the transition to take effect.
var s2 = document.querySelector("#section2");
setTimeout(() => {
s2.classList.add("up");
}, 2000);
html * {
transition: 1s all;
}
body {
overflow: hidden;
position: relative; /** ensure section#section2 is positioned relative to the body **/
}
#section1 {
background: steelblue;
}
#section2 {
background: indigo;
position: absolute;
top: 100vh; /** initial value **/
}
section {
width: 100%;
height: 100vh;
}
/** #section2.up is more specific **/
#section2.up {
top: 0;
}
<section id="section1"></section>
<section id="section2"></section>
You can also animate with an external library like jQuery:
$("#section2").animate({
top: 0,
duration: 2000
});
html * {
transition: 1s all;
}
body {
overflow: hidden;
}
#section1 {
background: steelblue;
}
#section2 {
background: indigo;
position: absolute;
}
section {
width: 100%;
height: 100vh;
}
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<section id="section1"></section>
<section id="section2"></section>
i'm trying to create a custom button for my portfolio.
This button will be animated, the top part sliding in from left and the bottom part sliding in from right when hovered.
At the center of it will be the Category linked to this button.
Here an illustration :
How can i set my text in the center for every that i will create ? I can't use "position: absolute" property cause the reference would be the webpage and not the position where the custom component is declared...
Here my actual code :
CSS
const CatStyle = styled.div`
box-shadow: 0px 0px 2px 2px rgba(0,0,0,0.75);
height: 50px;
max-width: 150px;
background-color: white;
display: flex;
justify-content: center;
flex-direction: column;
cursor: pointer;
transition : background-color 0.5s ease-out;
:hover div{
display: flex;
}
.catContent {
position: relative;
align-self: center;
font-size: 1.5rem;
}
.topSlideAnimation {
display: none
height: 50%;
background-color: green;
}
.botSlideAnimation {
transition : background-color 0.5s ease-out;
display: none;
height: 50%;
background-color: blue;
}
`
JSX
const ButtonCat = (props) => (
<CatStyle>
<div className="topSlideAnimation"></div>
<div className="catContent">Hello</div>
<div className="botSlideAnimation"></div>
</CatStyle>
)
Not done any jsx so not sure what your catstyle tag would render, but if you can just render a button (or div), I would do the following
Make an outer container that is flex (for aligning text in center)
Make an inner container that is for the text (so you can position it relatively and add a z-index to it)
Add pseudo elements to your outer container for the animated bits (rather than having 2 empty divs)
* {
box-sizing: border-box;
}
.button {
display: inline-flex; /* make flex for centring */
justify-content: center; /* vertically centre */
align-items: center; /* horizontally centre */
position: relative; /* for adding absolute positioned children */
min-height: 50px; /* test value to show text is centred */
overflow: hidden; /* hide pseudo elements when not shown */
}
.button:before,
.button:after {
content: ''; /* make coloured animatable bits */
display: block;
height: 50%;
width: 100%;
position: absolute;
transition: all 1s ease-in;
z-index: 1;
}
.button:before {
top: 0;
right: 100%;
background: grey;
}
.button:hover:before {
right: 0;
}
.button:after {
top: 50%;
left: 100%;
background: darkgrey;
}
.button:hover:after {
left: 0;
}
.text {
position: relative; /* needs to have a higher z-index than the pseduo elements so text appears on top */
z-index: 2;
}
<button class="button"><span class="text">test content</span></button>
I need to make a div square. The height of the div is dynamically changing and I want the width to be equal to the height. I have seen a lot of solutions to set the height to be equal to the width (with padding-bottom on pseudo-elements), but I need it the other way arround. Is this possible with pure CSS?
No .... well, there is this trick, where one use a hidden image
div {
display: inline-block;
height: 170px;
background: red;
}
div img {
visibility: hidden;
height: 100%;
}
<div>
<img src="http://placehold.it/50">
</div>
Updated
And here is a script version, that also keep it within the width
Stack snippet
(function (d,t) {
window.addEventListener("resize", throttler, false);
window.addEventListener("load", throttler(), false); /* run once on load to init */
function throttler() {
if ( !t ) {
t = setTimeout(function() {
t = null;
keepSquared(d.querySelector('.container'),
d.querySelector('.squared'));
}, 66);
}
}
function keepSquared(co,el) {
var s = window.getComputedStyle(co, null);
var m = Math.min(
parseFloat(s.getPropertyValue("width")),
parseFloat(s.getPropertyValue("height")));
el.style.cssText =
'width: ' + m + 'px; height: ' + m + 'px;';
}
})(document,null);
html, body {
height: 100%;
margin: 0;
}
.container {
position: relative;
width: 50%;
height: 50%;
top: 50px;
left: 50px;
border: 1px solid black;
box-sizing: border-box;
}
.squared {
background-color: red;
}
<div class="container">
<div class="squared">
</div>
</div>
Note: Since resize events can fire at a high rate, the throttler is used to reduced the rate so the handler doesn't execute expensive operations such as DOM modifications too often.