Recreating the Go-up effect on Netlify’s home page.

P10
6 min readJun 11, 2023
The text effect on netlify homepage we want to recreate.
The Effect I attempted to recreated

That’s the effect I am talking about after seeing this my friend instantly signed up for Netlify and he doesn’t even know what’s a frontend. Here will try to recreate this effect using CSS only(I cheated and used SCSS but only for a quality of life reason). Here is how it looks.

The effect that I created

This is just a way and probably neither the best way nor the way Netlify does it. There are lots of things not thought about like accessibility. Here is the code pen link, in this article I will try to walk you through this recreation.

Recreating this effect

Let’s dissect this animation, we have 3 different parts that we can see

  1. Grow Animation ( the gradient underline also grow with the div)
  2. Elevator go-up animation
  3. Shrink
  4. Background Gradient Dance.

Let's start with arguably the main animation — The elevator animation.

Elevator Go-Up Animation

The list items are moving up in animation . We create an element that moves up .train nested within .shaft element where overflow-y is clipped. To do this .train have position:absolute within .shaft which have position:relative to contain .train element within itself. Here is the HTML

 <div class="content">
I can pet
<div class="shaft">
<ol class="train">
<li>Cats</li>
<li>Dogs</li>
<li>Parrots</li>
<li>All my pets</li>
</ol>
</div>
!!!!!
</div>

Also here are a few things we need to take care of

  • The first child is visible when the animation is not playing. We do this by adding top:0px to the .train class
  • The animation stays in its final state once it's over. We simply set the animation-fill-mode toforwards.

Here is the CSS for .train and for its animation go-up

.train {
position: absolute;
animation: go-up 3s linear 3s forwards;
top:0px;
margin: 0;
padding: 0;
list-style: none;

}

@keyframes go-up {
from {
transform: translateY(0);
}
to {
transform: translateY(-100%) translateY(1lh);
/*translateY(1lh) so the animation stops before last element goes up*/

}
}

Here is the CSS for .shaft and .content

.content {
height: 1lh;
display: flex;

}

.shaft {
display: inline-block;
min-width: 10ch;
margin-left:1ch;

/*Important lines */
height: 1lh;
overflow-y: clip;
position: relative;
}

Codpen just for the elevator go-up animation only.

Gradient underline

Ideas that didn't work for this

  1. Add an underline: — CSS doesn't support gradient underlines yet
  2. Adding an ::after pseudo-element to the .shaft doesn't works due to the overflow-y: clip thing.

I propose we add a new element .elevator which is a parent of .shaftand with the sole purpose of gradient underline.

Here is the new structure

<div class="elevator">
<div class="shaft">
<ol class="train">
<li>Cats</li>
<li>Dogs</li>
<li>Parrots</li>
<li> Hamsters </li>
<li>any pet ever</li>
</ol>
</div>
</div>

The animation is simple here —

  1. For the pseudo-element just use Scale(0) and scale(100%)
  2. For the .elevator use change the width property.

code for .elevator and its gradient grows and shrink animation for the ::after pseudoelement and itself.

.elevator{ 
position:relative;
min-width: #{$--starting-width};
margin-left:1ch;
animation: grow #{$--expand-anim-dur} linear #{$--init-dur} forwards, shrink-to-final #{$--shrink-anim-dur} linear #{$--shrink-anim-delay} forwards;

}
@keyframes grow{
from{
width: #{$--starting-width};
}
to{
width:#{$--largest-width};
}
}
@keyframes shrink-to-final{

to{
width:#{$--final-wdith};
}
}


.elevator::after{
content:"";
display:block;
position: absolute;
width:100%;
height:10px;
background: var(--gradient-bg);

transform-origin:left;
transform:scaleX(0);
animation: expand #{$--expand-anim-dur} linear #{$--init-dur} forwards, shrink #{$--shrink-anim-dur} linear #{$--shrink-anim-delay} forwards ;

bottom:-0.5ch;
}
@keyframes expand{
from{
transform:scaleX(0);

}
to{

transform:scaleX(100%);
}
}
@keyframes shrink{
from{transform-origin:right;
transform:scaleX(100%)}
to{
transform-origin:right;
transform:scaleX(0);
}
}

Gradient Dance

Firstly we need to design a gradient that

  1. Turns black at the end
  2. Has a lighter tone in between

Here is what I used —

linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(1,1,11,1) 33%, rgba(9,9,121,1) 33%, rgba(0,212,255,1) 66%, rgba(9,9,121,1) 100%);
the gradient was created using https://cssgradient.io/
  1. 0–33% solid black is here so when we set the background size to 300%. The black is able to cover the complete text.

2. Rest of the gradient goes from dark shade to light and then dark again. This part will be 2X the size of the text. so there is enough space for the effect to be seen.

We cannot animate gradients in CSS but add the gradient to the background, set text-color to transparent, use background clip now we have gradient text. We animate this by moving the background. Here is the animation and element.

.train > li:last-child{
background:var(--gradient-large);
-webkit-background-clip:text;
background-size:300%;
color:transparent;
animation: gradient-move #{$--bg-dance-anim-dur} ease-in #{$--shrink-anim-delay};
}
@keyframes gradient-move{
from{
background-position:right;
}
to{
background-position:left;
}
}

Orchestrating this animation

There is a lot going on and we need to tie it all up. Basically setting the animation duration and adjusting the animation delay accordingly is important here. This example uses SCSS variables to track duration and to calculate delays.

Here is the order of animations happening

  1. expansion of .elevator + the expansion of gradient underline.
    Delay- $ — init-dur
    Duration- $ — expand-anim-dur
  2. The going-up effect
    Delay- $ — go-up-anim-delay:$ — init-dur+$ — expand-anim-dur
    Duration- $ — go-up-anim-dur
  3. Shrinking of .elevator + the shrinking of gradient underline.
    Delay- $ — shrink-anim-delay:$ — go-up-anim-delay+$ — go-up-anim-dur;
    Duration- $ — shrink-anim-dur
  4. Gradient text animation
    Delay- $ — shrink-anim-delay:$ — go-up-anim-delay+$ — go-up-anim-dur; starts with the previous animation but lasts longer.
    Duration- $ — bg-dance-anim-dur

Here is the end product

Codepen for elevator effect on netlify’s homepage.

Thanks for reading this article please provide feedback on it, and comment if you find a better way to do any of these parts.

Final Code

(in case you don't want to use codepen)

PS- I use a different way to position .train (so that initially only the first element is visible. and consequently the animation also slightly changes.

HTML

<div class="playground">
<div class="content">
Write
<div class="elevator">
<div class="shaft">
<ol class="train">
<li>Good</li>
<li>Engaging</li>
<li>Informative</li>
<li> Entertaining </li>
<li>Awesome</li>
</ol>
</div>
</div>
articles
</div>
</div>

SCSS


/* animation duration */
$--init-dur: 1s;
$--expand-anim-dur:1s;

$--go-up-anim-delay:$--init-dur+$--expand-anim-dur;
$--go-up-anim-dur: 2s;


$--shrink-anim-delay:$--go-up-anim-delay+$--go-up-anim-dur;
$--shrink-anim-dur: 500ms;
$--bg-dance-anim-dur:2.5s;

//Widths a bit hackish but cant think of another workaround
$--starting-width:5ch;
$--largest-width: 11ch;
$--final-wdith: 9ch;
* {
font-size: 2rem;
}
:root{

/* Gradients */
--gradient-bg: linear-gradient(90deg, hsla(217, 100%, 50%, 1) 0%, hsla(186, 100%, 69%, 1) 100%);
--gradient-large: linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 33%, rgba(9,9,121,1) 33%, rgba(0,212,255,1) 66%, rgba(9,9,121,1) 100%);
}
.playground {

height: 100vh;
display:grid;
place-items:center;
width: 100vw;
background: aliceblue;

}
.elevator{
position:relative;
min-width: #{$--starting-width};
margin-left:1ch;
animation: grow #{$--expand-anim-dur} linear #{$--init-dur} forwards, shrink-to-final #{$--shrink-anim-dur} linear #{$--shrink-anim-delay} forwards;
}

@keyframes grow{
from{
width: #{$--starting-width};
}
to{
width:#{$--largest-width};
}
}
@keyframes shrink-to-final{

to{
width:#{$--final-wdith};
}
}
.elevator::after{

content:"";
display:block;
position: absolute;
width:100%;
height:10px;
background: var(--gradient-bg);
transform-origin:left;
transform:scaleX(0);
animation: expand #{$--expand-anim-dur} linear #{$--init-dur} forwards, shrink #{$--shrink-anim-dur} linear #{$--shrink-anim-delay} forwards ;
// 500ms shrink 10s;
bottom:-0.5ch;
}
@keyframes expand{
from{
transform:scaleX(0);

}
to{

transform:scaleX(100%);
}
}

@keyframes shrink{
from{transform-origin:right;
transform:scaleX(100%)}
to{
transform-origin:right;
transform:scaleX(0);
}
}

.content {
height: 1lh;
display: flex;
font-weight: bold;

}

.shaft {
width:100%;
display: inline-block;
height: 1lh;
overflow-y: clip;
position: relative;
}


.train {
position: absolute;
animation:#{$--go-up-anim-dur} go-up ease-out #{$--go-up-anim-delay} forwards;
transform: translateY(100%) translateY(-1lh);
bottom:0px;
margin: 0;
padding: 0;
list-style: none;

}
@keyframes go-up {

to {
transform: translate(0);

}
}


.train > li:last-child{
background:var(--gradient-large);
-webkit-background-clip:text;
background-size:300%;
color:transparent;
animation: gradient-move #{$--bg-dance-anim-dur} ease-in #{$--shrink-anim-delay};
}

@keyframes gradient-move{
from{
background-position:right;
}
to{
background-position:left;
}
}

--

--

P10

Into Webdev and realted stuff. Learning JS & web3. My blogs are journal to my learning journey.