- Published on
CSS in real world - Re-create Vercel Develop. Preview. Ship. with Tailwind CSS
- Authors
- Name
- Thang Huu Vu
- @thanghvu
Table of Contents
Intro
Earlier this week I tweeted about a Tailwind Play version of Vercel "Develop. Preview. Ship" Hero Header. Here is how it looks like on Vercel Homepage:
Seeing simple yet effective CSS like this is always fascinating to me. I eventually adapted the animation for this site's Hero. Let's discuss the technical details behind this piece as we re-create it using Tailwind.
Goal
Let's aim for the closest we can get to achieve this look. I will cover:
- Typography
- Gradient
- Animation
Breakdown
From the Vercel homepage, we can see that the markup looks like this:
<h1 class="hero_title__2dqLj" aria-label="Develop. Preview. Ship.">
<span
class="animated-gradient-text_background__104Eo animated-gradient-text_background-1__2q1-A"
style="--content:'Develop.';--padding:0.05em;--start-color:#007CF0;--end-color:#00DFD8"
>
::before
<span
class="animated-gradient-text_foreground__2kjjY animated-gradient-text_foreground-1__3O_nT"
>Develop.</span
></span
><span
class="animated-gradient-text_background__104Eo animated-gradient-text_background-2__3r8da"
style="--content:'Preview.';--padding:0.05em;--start-color:#7928CA;--end-color:#FF0080"
>
::before
<span
class="animated-gradient-text_foreground__2kjjY animated-gradient-text_foreground-2__BYeW7"
>Preview.</span
></span
><span
class="animated-gradient-text_background__104Eo animated-gradient-text_background-3__3Bvxj"
style="--content:'Ship.';--padding:0.05em;--start-color:#FF4D4D;--end-color:#F9CB28"
>
::before
<span
class="animated-gradient-text_foreground__2kjjY animated-gradient-text_foreground-3__3Khgf"
>Ship.</span
></span
>
</h1>
Noted that each span
element contains a descendant span
and a pseudo-element. While the outer span
contains information about gradient colors, the ::before
pseudo-element is holding the black text (background), and the inner span
is holding the gradient text (foreground). The background and foreground take turns to show on the screen, consecutively between the three words.
Implementation
Typography
- Font choice: Vercel uses Inter font. (I skip this).
- Font weight: extra-bold (800)
- Font size:
23vw
for default (mobile) and10rem
for desktop - Letter spacing:
0.06rem
.
The last two do not default in Tailwind. Therefore, you need to customize it. Here are two ways you can customize utility classes:
- Use bracket
- Extend configuration
Let's extend letterSpacing
configuration:
module.exports = {
theme: {
extend: {
fontSize: {
'10xl': '10rem',
},
letterSpacing: {
tightest: '-.06em',
},
}
}
}
And create the markup using the customization:
<h1 class="py-14 text-[23vw] sm:text-10xl leading-none select-none tracking-tightest font-extrabold text-center">
<span class="relative block">
<span class="px-2">
Develop.
</span>
</span>
<span class="relative block">
<span class="px-2">
Preview.
</span>
</span>
<span class="relative block">
<span class="px-2">
Ship.
</span>
</span>
</h1>
Here is how it should look like for now.
Gradient
To achieve the gradient background effect for the foreground text, we combine the Gradient Stops and Background Clip utilities. Via inspection, we can extract the colors and add them to the config:
module.exports = {
theme: {
extend: {
fontSize: {
'10xl': '10rem',
},
letterSpacing: {
tightest: '-.06em',
},
gradientColorStops: {
'gradient-1-start': '#007CF0',
'gradient-1-end': '#00DFD8',
'gradient-2-start': '#7928CA',
'gradient-2-end': '#FF0080',
'gradient-3-start': '#FF4D4D',
'gradient-3-end': '#F9CB28',
},
}
}
}
Applying back these to the markup:
<h1 class="py-14 text-[23vw] sm:text-10xl leading-none select-none tracking-tightest font-extrabold text-center">
<span class="relative block">
<span class="px-2 text-transparent bg-clip-text bg-gradient-to-r from-gradient-1-start to-gradient-1-end"> Develop.</span>
</span>
<span class="relative block">
<span class="px-2 text-transparent bg-clip-text bg-gradient-to-r from-gradient-2-start to-gradient-2-end"> Preview.</span>
</span>
<span class="relative block">
<span class="px-2 text-transparent bg-clip-text bg-gradient-to-r from-gradient-3-start to-gradient-3-end"> Ship.</span>
</span>
</h1>
What is going on? For the foreground, we set the background color to gradient, using utilities from-
and to-
. Then, we use text-transparent
and bg-clip-text
to clip the text & achieve the gradient text effect.
Tip: Besides
from-
andto-
, you can also usevia-
to add a middle color in your design.
We are making good progress! Now the text should have vivid gradient colors. Here is how it should look like.
Animation
This is the most exciting part. It should be clear now how the animations are designed. You can see the timeline for animation using Firefox inspector tools. Open the inspector window and check the Animation tab.
Take the first span
as an example. Here we can see background
and foreground
animations:
Fig. 1.1 The foreground animation
Fig. 1.2 The foreground animation
The figures show that the two animations overlapping each other (see the charts). Before the foreground appears, the background smoothly fades in. This keeps running infinitely, with an 8-sec interval.
::before
pseudo element
Let's first style the pseudo-element:
<h1 class="py-14 text-[23vw] text-center sm:text-10xl leading-none select-none tracking-tightest font-extrabold">
<span data-content="Develop." class="relative block before:content-[attr(data-content)] before:w-full before:z-0 before:block before:absolute before:top-0 before:bottom-0 before:left-0 before:text-center before:text-black">
<span class="px-2 text-transparent bg-clip-text bg-gradient-to-br from-gradient-1-start to-gradient-1-end"> Develop.</span>
</span>
<span data-content="Preview." class="relative block before:content-[attr(data-content)] before:w-full before:z-0 before:block before:absolute before:top-0 before:bottom-0 before:left-0 before:text-center before:text-black">
<span class="px-2 text-transparent bg-clip-text bg-gradient-to-br from-gradient-2-start to-gradient-2-end"> Preview.</span>
</span>
<span data-content="Ship." class="relative block before:content-[attr(data-content)] before:w-full before:z-0 before:block before:absolute before:top-0 before:bottom-0 before:left-0 before:text-center before:text-black">
<span class="px-2 text-transparent bg-clip-text bg-gradient-to-br from-gradient-3-start to-gradient-3-end"> Ship.</span>
</span>
</h1>
- We want the pseudo-element to be
absolute
positioned within the outerspan
. - Use
data content
andbefore:content-[attr(data-content)]
to fill content inside pseudo-elements. - You should see the display is all black now, as the pseudo-element is overlaying the descendant
span
. This is expected.
Animations
Animation utilities in Tailwind CSS are enabled by default. To write custom animation, go to tailwind.config.js
. We need to extend keyframes
& animation
. The value are extracted from Vercel:
module.exports = {
theme: {
extend: {
fontSize: {
'10xl': '10rem',
},
letterSpacing: {
tightest: '-.06em',
},
gradientColorStops: {
'gradient-1-start': '#007CF0',
'gradient-1-end': '#00DFD8',
'gradient-2-start': '#7928CA',
'gradient-2-end': '#FF0080',
'gradient-3-start': '#FF4D4D',
'gradient-3-end': '#F9CB28',
},
keyframes: {
'gradient-foreground-1': {
'from, 16.667%, to': {
opacity: 1,
},
'33.333%, 83.333%': {
opacity: 0,
},
},
'gradient-background-1': {
'from, 16.667%, to': {
opacity: 0,
},
'25%, 91.667%': {
opacity: 1,
},
},
'gradient-foreground-2': {
'from, to': {
opacity: 0,
},
'33.333%, 50%': {
opacity: 1,
},
'16.667%, 66.667%': {
opacity: 0,
},
},
'gradient-background-2': {
'from, to': {
opacity: 1,
},
'33.333%, 50%': {
opacity: 0,
},
'25%, 58.333%': {
opacity: 1,
},
},
'gradient-foreground-3': {
'from, 50%, to': {
opacity: 0,
},
'66.667%, 83.333%': {
opacity: 1,
},
},
'gradient-background-3': {
'from, 58.333%, 91.667%, to': {
opacity: 1,
},
'66.667%, 83.333%': {
opacity: 0,
},
},
},
animation: {
'gradient-background-1': 'gradient-background-1 8s infinite',
'gradient-foreground-1': 'gradient-foreground-1 8s infinite',
'gradient-background-2': 'gradient-background-2 8s infinite',
'gradient-foreground-2': 'gradient-foreground-2 8s infinite',
'gradient-background-3': 'gradient-background-3 8s infinite',
'gradient-foreground-3': 'gradient-foreground-3 8s infinite',
}
}
}
}
It's time to apply the class back to our markup. Here is how it should look like at the end. Spoiler alert: The classnames are long 😆
Conclusion
You can see the source code of this site Hero to see how I implemented it. Hope you enjoyed the read.