43
loading...
This website collects cookies to deliver better user experience
@scroll-timeline
property available so far only in Chrome 94+, I have seen an interesting demo by @bramus
where the user is aware of the time needed to read a page with a sticky progress bar that stretches horizontally, whose width is dependant on the current scrolling position of the root
element.--ss
and --mm
hold the values of seconds and minutes and two distinct animations change them. In order to make the interpolation of the values work in the keyframes, these variables have been registered as integers
properties through the Houdini's API integrated on Chrome.--ss
variable is declared in the CSS like this:@property --ss {
syntax: "<integer>";
initial-value: 0;
inherits: true;
}
--mm
is declared instead in a <style>
block, inside the markup because here is where we are going to set the minutes needed (e.g. 7
):<style>
@property --mm {
syntax: "<integer>";
initial-value: 7;
inherits: true;
}
</style>
content
property I've set two counters to print their values through the counter()
function (with a leading 0
when necessary)counter-reset: ss var(--ss);
...
content: counter(ss, decimal-leading-zero);
counter-reset: mm var(--mm);
...
content: counter(mm, decimal-leading-zero);
@scroll-timeline scroll {
time-range: 60s;
start: 200px;
end : calc(100% - 150px);
}
time-range
has been set to 60
seconds in order to make the calculations of the animations easier (at least for my brain) . 200px
of scroll — to exclude the title or maybe a hero image, but you could even set it to 0
or to another value of course — and we want to also stop it before a hypothetical footer 150px
tall.@keyframes mins {
to { --mm: 0; }
}
...
animation: mins
/* duration */ 60s
/* timing */ steps(var(--mm), start)
/* delay */ calc(calc(60s / (calc(var(--mm)) - 1)) / 60 / var(--mm))
/* repetition */ 1
/* fill mode */ forwards;
@keyframes
start from the value we have previously set and should decrease until 0
, so the definition of the last keyframe is enough.60s
which is the same amount of time we defined for the @scroll-timeline
. The delay
expression is calculated so that the value of the minutes decreases immediately after 1s
of reading time (e.g. switching from 7:00
to 6:59
), which is good to point out, it is not one second of animation (we will discuss about this in the next section).@keyframes secs {
from { --ss: 59; }
to { --ss: 0; }
}
...
animation: secs
/* duration */ calc(60s / calc(var(--mm)))
/* timing */ linear
/* delay */ calc(calc(60s / (calc(var(--mm)) - 1)) / 60 / var(--mm))
/* repetition */ var(--mm)
/* fill-mode */ forwards;
--mm
times from 59
to 0
with a repetition
of --mm
. Here the purpose of the delay
is just to wait the first update of the minutes.calc(calc(60s / (calc(var(--mm)) - 1)) / 60 / var(--mm))
60s
and we need to wait 1s
of reading time. 60s / (--mm - 1)
of the animation (because the value of minutes changes immediately by -1);1/60
of the value we have just obtained, so we divide it by 60
and we get 60s / (--mm - 1) / 60
;60s / (--mm - 1) / 60 / --mm
.@media (prefers-reduced-motion: reduce) {
.countdown__display--ss {
display: none;
}
}
“Ta-da!”
@scroll-timeline
: