16 CSS Bounce Animations01 / 16
Scroll Down Arrow Bounce
A landing-page scroll-down indicator — a chevron arrow bouncing gently downward in an infinite loop, signaling "there's more content below." Deep purple → magenta gradient surface with the arrow as the visual anchor. The canonical Apple / Vercel / Linear landing-page pattern users search for when building marketing pages. Uses translateY with a multi-stop bounce keyframe — never margin-top or padding-top which would trigger layout on every frame.
Published
The code
<div class="bn-01">
<p class="bn-01__hint">You're at the top</p>
<button class="bn-01__scroll" type="button" aria-label="Scroll to next section">
<span class="bn-01__label">Scroll</span>
<span class="bn-01__arrow" aria-hidden="true">
<svg viewBox="0 0 40 40" width="40" height="40" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<polyline points="10 15 20 25 30 15"/>
</svg>
</span>
</button>
</div><div class="bn-01">
<p class="bn-01__hint">You're at the top</p>
<button class="bn-01__scroll" type="button" aria-label="Scroll to next section">
<span class="bn-01__label">Scroll</span>
<span class="bn-01__arrow" aria-hidden="true">
<svg viewBox="0 0 40 40" width="40" height="40" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<polyline points="10 15 20 25 30 15"/>
</svg>
</span>
</button>
</div>.bn-01 {
--ink: #f5f0ff;
--sub: rgba(245,240,255,.6);
--accent: #f9a8d4;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 80px;
padding: 64px 32px;
background: radial-gradient(60% 60% at 50% 50%, rgba(249,168,212,.18) 0%, transparent 65%), linear-gradient(135deg,#3b0764 0%,#5b21b6 45%,#831843 100%);
color: var(--ink);
text-align: center;
}
.bn-01 *,
.bn-01 *::before,
.bn-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-01 ::selection {
background: var(--accent);
color: #3b0764;
}
.bn-01__hint {
font-size: .9rem;
color: var(--sub);
letter-spacing: .06em;
font-weight: 500;
}
.bn-01__scroll {
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 14px;
min-width: 88px;
padding: 14px 24px;
background: transparent;
border: none;
color: var(--ink);
cursor: pointer;
font-family: inherit;
transition: opacity .18s ease;
}
.bn-01__scroll:hover {
opacity: .75;
}
.bn-01__scroll:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 8px;
border-radius: 8px;
}
.bn-01__label {
font-size: .75rem;
font-weight: 700;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--accent);
}
.bn-01__arrow {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--ink);
animation: bn-01-bounce 2.4s cubic-bezier(.55,0,.45,1) infinite;
}
@keyframes bn-01-bounce {
0% {
transform: translateY(0);
}
30% {
transform: translateY(-14px);
}
50% {
transform: translateY(-4px);
}
70% {
transform: translateY(-10px);
}
100% {
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.bn-01__arrow {
animation: bn-01-pulse 3s ease-in-out infinite;
}
@keyframes bn-01-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: .55;
}
}
}.bn-01 {
--ink: #f5f0ff;
--sub: rgba(245,240,255,.6);
--accent: #f9a8d4;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 80px;
padding: 64px 32px;
background: radial-gradient(60% 60% at 50% 50%, rgba(249,168,212,.18) 0%, transparent 65%), linear-gradient(135deg,#3b0764 0%,#5b21b6 45%,#831843 100%);
color: var(--ink);
text-align: center;
}
.bn-01 *,
.bn-01 *::before,
.bn-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-01 ::selection {
background: var(--accent);
color: #3b0764;
}
.bn-01__hint {
font-size: .9rem;
color: var(--sub);
letter-spacing: .06em;
font-weight: 500;
}
.bn-01__scroll {
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 14px;
min-width: 88px;
padding: 14px 24px;
background: transparent;
border: none;
color: var(--ink);
cursor: pointer;
font-family: inherit;
transition: opacity .18s ease;
}
.bn-01__scroll:hover {
opacity: .75;
}
.bn-01__scroll:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 8px;
border-radius: 8px;
}
.bn-01__label {
font-size: .75rem;
font-weight: 700;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--accent);
}
.bn-01__arrow {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--ink);
animation: bn-01-bounce 2.4s cubic-bezier(.55,0,.45,1) infinite;
}
@keyframes bn-01-bounce {
0% {
transform: translateY(0);
}
30% {
transform: translateY(-14px);
}
50% {
transform: translateY(-4px);
}
70% {
transform: translateY(-10px);
}
100% {
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.bn-01__arrow {
animation: bn-01-pulse 3s ease-in-out infinite;
}
@keyframes bn-01-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: .55;
}
}
}Here's a working CSS Bounce Animation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Scroll Down Arrow Bounce
Source: https://codefronts.com/motion/css-bounce-animation/scroll-down-arrow-bounce/
A landing-page scroll-down indicator — a chevron arrow bouncing gently downward in an infinite loop, signaling "there's more content below." Deep purple → magenta gradient surface with the arrow as the visual anchor. The canonical Apple / Vercel / Linear landing-page pattern users search for when building marketing pages. Uses translateY with a multi-stop bounce keyframe — never margin-top or padding-top which would trigger layout on every frame.
## HTML
```html
<div class="bn-01">
<p class="bn-01__hint">You're at the top</p>
<button class="bn-01__scroll" type="button" aria-label="Scroll to next section">
<span class="bn-01__label">Scroll</span>
<span class="bn-01__arrow" aria-hidden="true">
<svg viewBox="0 0 40 40" width="40" height="40" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<polyline points="10 15 20 25 30 15"/>
</svg>
</span>
</button>
</div>
```
## CSS
```css
.bn-01 {
--ink: #f5f0ff;
--sub: rgba(245,240,255,.6);
--accent: #f9a8d4;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 80px;
padding: 64px 32px;
background: radial-gradient(60% 60% at 50% 50%, rgba(249,168,212,.18) 0%, transparent 65%), linear-gradient(135deg,#3b0764 0%,#5b21b6 45%,#831843 100%);
color: var(--ink);
text-align: center;
}
.bn-01 *,
.bn-01 *::before,
.bn-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-01 ::selection {
background: var(--accent);
color: #3b0764;
}
.bn-01__hint {
font-size: .9rem;
color: var(--sub);
letter-spacing: .06em;
font-weight: 500;
}
.bn-01__scroll {
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 14px;
min-width: 88px;
padding: 14px 24px;
background: transparent;
border: none;
color: var(--ink);
cursor: pointer;
font-family: inherit;
transition: opacity .18s ease;
}
.bn-01__scroll:hover {
opacity: .75;
}
.bn-01__scroll:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 8px;
border-radius: 8px;
}
.bn-01__label {
font-size: .75rem;
font-weight: 700;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--accent);
}
.bn-01__arrow {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--ink);
animation: bn-01-bounce 2.4s cubic-bezier(.55,0,.45,1) infinite;
}
@keyframes bn-01-bounce {
0% {
transform: translateY(0);
}
30% {
transform: translateY(-14px);
}
50% {
transform: translateY(-4px);
}
70% {
transform: translateY(-10px);
}
100% {
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.bn-01__arrow {
animation: bn-01-pulse 3s ease-in-out infinite;
}
@keyframes bn-01-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: .55;
}
}
}
```Here's a working CSS Bounce Animation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Scroll Down Arrow Bounce
Source: https://codefronts.com/motion/css-bounce-animation/scroll-down-arrow-bounce/
A landing-page scroll-down indicator — a chevron arrow bouncing gently downward in an infinite loop, signaling "there's more content below." Deep purple → magenta gradient surface with the arrow as the visual anchor. The canonical Apple / Vercel / Linear landing-page pattern users search for when building marketing pages. Uses translateY with a multi-stop bounce keyframe — never margin-top or padding-top which would trigger layout on every frame.
## HTML
```html
<div class="bn-01">
<p class="bn-01__hint">You're at the top</p>
<button class="bn-01__scroll" type="button" aria-label="Scroll to next section">
<span class="bn-01__label">Scroll</span>
<span class="bn-01__arrow" aria-hidden="true">
<svg viewBox="0 0 40 40" width="40" height="40" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<polyline points="10 15 20 25 30 15"/>
</svg>
</span>
</button>
</div>
```
## CSS
```css
.bn-01 {
--ink: #f5f0ff;
--sub: rgba(245,240,255,.6);
--accent: #f9a8d4;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 80px;
padding: 64px 32px;
background: radial-gradient(60% 60% at 50% 50%, rgba(249,168,212,.18) 0%, transparent 65%), linear-gradient(135deg,#3b0764 0%,#5b21b6 45%,#831843 100%);
color: var(--ink);
text-align: center;
}
.bn-01 *,
.bn-01 *::before,
.bn-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-01 ::selection {
background: var(--accent);
color: #3b0764;
}
.bn-01__hint {
font-size: .9rem;
color: var(--sub);
letter-spacing: .06em;
font-weight: 500;
}
.bn-01__scroll {
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 14px;
min-width: 88px;
padding: 14px 24px;
background: transparent;
border: none;
color: var(--ink);
cursor: pointer;
font-family: inherit;
transition: opacity .18s ease;
}
.bn-01__scroll:hover {
opacity: .75;
}
.bn-01__scroll:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 8px;
border-radius: 8px;
}
.bn-01__label {
font-size: .75rem;
font-weight: 700;
letter-spacing: .2em;
text-transform: uppercase;
color: var(--accent);
}
.bn-01__arrow {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--ink);
animation: bn-01-bounce 2.4s cubic-bezier(.55,0,.45,1) infinite;
}
@keyframes bn-01-bounce {
0% {
transform: translateY(0);
}
30% {
transform: translateY(-14px);
}
50% {
transform: translateY(-4px);
}
70% {
transform: translateY(-10px);
}
100% {
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.bn-01__arrow {
animation: bn-01-pulse 3s ease-in-out infinite;
}
@keyframes bn-01-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: .55;
}
}
}
```How this works
The arrow lives inside a real <button aria-label="Scroll to next section"> and animates via a 5-stop @keyframes bn-01-bounce cascade: 0% translateY(0) → 30% translateY(-14px) → 50% translateY(-4px) → 70% translateY(-10px) → 100% translateY(0). Two upward peaks with the second smaller — the classic "decaying bounce" energy signature that reads as physical. Cycle 2.4 seconds with cubic-bezier(.55, 0, .45, 1). Slower than an urgent alert; slower reads as "ambient invitation."
Deep purple → magenta gradient background (#3b0764 → #831843) — agency / creative-studio landing palette. The demo shows a large centered chevron with a small "SCROLL" label above it and a subtle "You're at the top" hint text so the demo purpose reads at a glance. 44×44px tap target per WCAG 2.5.5. Semantic HTML with real button element and aria-label. On prefers-reduced-motion, the vertical bounce swaps for a gentle opacity pulse.
Make it yours
- Change bounce height by editing
-14px— 8px for subtle, 20px for playful. Any value works becausetranslateYis compositor-safe. - Slow the loop by editing
animation-duration: 2.4s— 3s reads as more meditative, 1.8s more urgent. Below 1.5s reads as jittery. - For horizontal scroll cues (Apple iPad marketing / horizontal-scroll landing pages), swap
translateYfortranslateXwith a rightward chevron. - Add a fade-out when the user scrolls past the hero — use
Intersection Observerto toggleopacity: 0. Prevents the arrow from persisting into second-fold content. - For Tailwind:
animate-[bn01_2.4s_cubic-bezier(.55,0,.45,1)_infinite]with matchingkeyframes.bn01registered intailwind.config.js. See FAQ #4.
Gotchas — read before shipping
- NEVER animate
margin-top,padding-top, ortopto create the bounce — every frame triggers the browser's full layout + paint + composite pipeline. On mid-tier mobile that's 15-20ms per frame, tanking INP scores below 200ms (Google's "Poor" threshold).transform: translateYruns on the GPU compositor thread with zero layout cost. - For accessibility, wrap the arrow in a real
<button>with anaria-label— not just a<div>. Screen-reader users need to know this is interactive. - The 44×44px minimum touch target (WCAG 2.5.5) applies here — even though the chevron icon is only 24px, button padding brings the tappable area to 44×44px. Mobile users with motor impairments miss taps otherwise.
- Respect
prefers-reduced-motion— vestibular-disorder users find bouncing arrows nauseating. The demo replaces the bounce with a subtle opacity pulse (100% → 60% → 100%) that still conveys "there's more below" without vertical motion. - For infinite-scroll pages, the scroll indicator must hide once the user has scrolled — otherwise it reads as "there's more" on the second and third fold when actually the page is loading more. Use
Intersection Observeron the hero to toggle visibility. - The chevron uses
stroke-linejoin: roundandstroke-linecap: roundso the corners don't feel harsh. Sharp corners on animated elements amplify the motion feel; rounded corners feel softer.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 45+ | 10+ | 40+ | 45+ |
transform + @keyframes. Universal, GPU-accelerated.