Tutorial
Recreating Vercel's Smooth Navigation Animation With CSS Only
In this video we'll be recreating an animation that shines in its simplicity.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697import { FefyiLogo } from "./fefyi";
import "./index.css";
import useScrollPosition from "@react-hook/window-scroll";
import { useMemo } from "react";
const useRange = (
num: number,
inMin: number,
inMax: number,
outMin: number,
outMax: number,
) => {
const mappedValue = useMemo(() => {
const newValue =
((num - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
const largest = Math.max(outMin, outMax);
const smallest = Math.min(outMin, outMax);
return Math.min(Math.max(newValue, smallest), largest);
}, [num]);
return mappedValue;
};
function App() {
const y = useScrollPosition(60);
const navX = useRange(y, 0, 50, 0, 42);
const logoScale = useRange(y, 0, 50, 1, 0.8);
return (
<div className="font-sans">
<header className="flex gap-4 bg-black px-6 py-4 pl-16 text-sm">
<svg
style={{
transform: `scale(${logoScale})`,
}}
className="fixed left-6 top-4 z-10"
aria-label="Vercel Logo"
fill="white"
viewBox="0 0 75 65"
height="22"
>
<path d="M37.59.25l36.95 64H.64l36.95-64z"></path>
</svg>
<ol className="flex gap-4">
<li aria-hidden className="text-zinc-700">
/
</li>
<li className="flex items-center gap-2">
<span className="inline-flex h-5 w-5 items-center justify-center rounded-full border border-zinc-700 bg-black p-1">
<FefyiLogo />
</span>
frontendfyi
</li>
<li aria-hidden className="text-zinc-700">
/
</li>
<li className="flex items-center gap-2">
<span className="inline-flex h-5 w-5 items-center justify-center rounded-full border border-zinc-700 bg-black p-1">
<FefyiLogo />
</span>
courses
</li>
</ol>
</header>
<nav className="sticky top-0 flex border-b border-zinc-700 bg-zinc-900 text-sm">
<ol
style={{
transform: `translateX(${navX}px)`,
}}
className="relative flex gap-4 px-6 py-4 text-zinc-400"
>
<li>Project</li>
<li className="text-zinc-200">Deployments</li>
<li>Analytics</li>
<li>Speed Insights</li>
<li>Logs</li>
<li>Storage</li>
<li>Settings</li>
</ol>
</nav>
<main className="mx-auto my-12 max-w-5xl rounded-md border border-zinc-800">
<table className="w-full">
<tbody>
{[...Array(100)].map((_, i) => (
<tr key={i} className="h-12 border-b border-zinc-800 bg-zinc-950">
<td />
</tr>
))}
</tbody>
</table>
</main>
</div>
);
}
export default App;
This week I noticed Vercel added a new scroll animation to their deployments page. My mind immediately thought this was such a nice animation that I wanted to recreate with Framer Motion. Then I thought; Why Framer Motion?! This is such a simple animation, we can do this with plain CSS!
That is exactly what we will be exploring in todays video. We are gonna recreate this smooth scroll animation with just a few lines of CSS and a single JavaScript scroll listener. Let’s dive in!
Eager to learn more?
I think this video might be a good fit for you too!
