-->
Recently, I’ve been really fascinated by the detail of Duolingo’s UI, especially the UI animation. I noticed even the buttons’ animations feels smooth. I was motivated to recreate the button and learn along the way.
So, here’s what I built for this post:
In this post, I’ll share how I made the button styles using HTML and CSS.
First, let’s start with a simple HTML button element with styles:
<!-- HTML -->
<button class="button">Click Me</button>
/* CSS */
:root {
--color-primary: #4dabf7;
--color-primary-hover: #339af0;
--color-shadow: #1c7ed6;
--color-text: #fff;
}
.button {
background-color: var(--color-primary);
color: var(--color-text);
font-weight: 700;
letter-spacing: 0.5px;
border-radius: 10px;
padding: 0.5rem 1rem;
}
I added CSS variables to make managing colors easier.
However this is not the initial appearance we want for the component we’re aiming to build. The Duolingo button
has a “depth” effect. We could achieve the looks by using several approaches. For instance, we may attempt
using the border-bottom-width
property to add the depth effect.
Personally, I don’t think that’s a good idea. The border
property will push other elements and the changes made
to its value will also affected the whole layout. So, I decided to use the box-shadow
and transform
properties instead:
/* CSS */
:root {
/* ... */
}
.button {
/* ... */
/* Styles for adding depth: */
box-shadow: 0px 4px var(--color-shadow);
transform: translateY(-4px);
}
The box-shadow
property is used to create the “depth” effect; it’s the part that draw the button’s z-axis part.
Tha transform
property is used to lift the button from its normal inline flow.
hover
state stylesThe first interaction I wanted to add is the hover state. I chose the hover state simply because it’s the first state that will be triggered when the user intended to click the button.
We can use just add a :hover
pseudo-class to our CSS to add the hover effect:
/* CSS */
:root {
/* ... */
}
.button {
/* ... */
}
/* Hover styles */
.button:hover {
background-color: var(--color-primary-hover);
cursor: pointer;
}
active
state stylesThe final step — the most exiciting step — is to add the active
state’s styles!
First, let’s add the pseudo-class :active
to the .button
class to add styles when
the button is clicked:
/* CSS */
:root {
/* ... */
}
.button {
/* ... */
}
.button:hover {
/* ... */
}
/* Clicked (active) styles */
.button:active {
box-shadow: 0px 0px var(--color-shadow);
background-color: var(--color-primary-hover);
transform: translateY(0px);
}
Looks good! So, we change the box-shadow
and transform
properties.
First, the box-shadow
’s value is changed so we create an effect of pressed down.
Then, the transform
value also changed to give a sense that the button pressed down to
it’s normal inline flow.
However, personally, I find the transition to be slightly odd. Let’s add the final touch — animation:
/* CSS */
:root {
/* ... */
}
.button {
/* ... */
}
.button:hover {
/* ... */
}
/* Clicked (active) styles */
.button:active {
/* ... */
animation: buttonClicked 0.15s;
}
@keyframes buttonClicked {
0% {
transform: translateY(-4px);
box-shadow: 0px 4px var(--color-shadow);
}
100% {
transform: translateY(0px);
box-shadow: 0px 0px var(--color-shadow);
}
}
Perfect! 💯💯💯
To wrap up, we’ve recreated Duolingo’s button using HTML, CSS, and CSS Animation. I hope you find benefit from this short post. Thanks!