16 CSS Bounce Animations12 / 16
Floating Hovering Element
A soft, weightless bounce that loops indefinitely to make an illustration, badge, or icon look like it's floating in mid-air. Sky-blue soft pastel palette with a rounded card element that gently rises and falls in an infinite ease-in-out cycle. Perfect for hero illustrations, achievement badges, mascots, or any element that needs to feel "alive" without demanding attention. Uses alternate iteration so the up-and-down motion never resets — smooth continuous drift.
Published
The code
<div class="bn-12">
<p class="bn-12__label">Weightless float loop</p>
<div class="bn-12__badge" aria-hidden="true">
<svg viewBox="0 0 24 24" width="44" height="44" fill="currentColor" aria-hidden="true">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
</div>
<p class="bn-12__hint">The badge rises and falls infinitely with <code>alternate</code> iteration.</p>
</div><div class="bn-12">
<p class="bn-12__label">Weightless float loop</p>
<div class="bn-12__badge" aria-hidden="true">
<svg viewBox="0 0 24 24" width="44" height="44" fill="currentColor" aria-hidden="true">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
</div>
<p class="bn-12__hint">The badge rises and falls infinitely with <code>alternate</code> iteration.</p>
</div>.bn-12 {
--page-bg: #e0f2fe;
--surface: #ffffff;
--ink: #075985;
--sub: #0369a1;
--accent: #0ea5e9;
--accent-hi: #38bdf8;
--shadow: rgba(2,132,199,.35);
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: 24px;
padding: 32px;
background: radial-gradient(60% 60% at 50% 40%, rgba(14,165,233,.18) 0%, transparent 60%), var(--page-bg);
color: var(--ink);
text-align: center;
}
.bn-12 *,
.bn-12 *::before,
.bn-12 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-12 ::selection {
background: var(--accent);
color: #fff;
}
.bn-12__label {
font-size: .9rem;
font-weight: 600;
color: var(--sub);
letter-spacing: .02em;
}
.bn-12__badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 120px;
height: 120px;
border-radius: 32px;
background: linear-gradient(135deg,var(--accent-hi),var(--accent));
color: #fff;
box-shadow: 0 20px 40px -12px var(--shadow);
animation: bn-12-float 2.4s ease-in-out infinite alternate, bn-12-shadow 2.4s ease-in-out infinite alternate;
will-change: transform;
}
@keyframes bn-12-float {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-16px);
}
}
@keyframes bn-12-shadow {
0% {
box-shadow: 0 20px 40px -12px var(--shadow);
}
100% {
box-shadow: 0 32px 52px -14px var(--shadow);
}
}
.bn-12__hint {
font-size: .78rem;
color: var(--sub);
max-width: 360px;
line-height: 1.5;
opacity: .85;
}
.bn-12__hint code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
padding: 1px 5px;
border-radius: 4px;
background: rgba(14,165,233,.14);
color: var(--sub);
}
@media (prefers-reduced-motion: reduce) {
.bn-12__badge {
animation: none!important;
transform: none!important;
}
}.bn-12 {
--page-bg: #e0f2fe;
--surface: #ffffff;
--ink: #075985;
--sub: #0369a1;
--accent: #0ea5e9;
--accent-hi: #38bdf8;
--shadow: rgba(2,132,199,.35);
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: 24px;
padding: 32px;
background: radial-gradient(60% 60% at 50% 40%, rgba(14,165,233,.18) 0%, transparent 60%), var(--page-bg);
color: var(--ink);
text-align: center;
}
.bn-12 *,
.bn-12 *::before,
.bn-12 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-12 ::selection {
background: var(--accent);
color: #fff;
}
.bn-12__label {
font-size: .9rem;
font-weight: 600;
color: var(--sub);
letter-spacing: .02em;
}
.bn-12__badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 120px;
height: 120px;
border-radius: 32px;
background: linear-gradient(135deg,var(--accent-hi),var(--accent));
color: #fff;
box-shadow: 0 20px 40px -12px var(--shadow);
animation: bn-12-float 2.4s ease-in-out infinite alternate, bn-12-shadow 2.4s ease-in-out infinite alternate;
will-change: transform;
}
@keyframes bn-12-float {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-16px);
}
}
@keyframes bn-12-shadow {
0% {
box-shadow: 0 20px 40px -12px var(--shadow);
}
100% {
box-shadow: 0 32px 52px -14px var(--shadow);
}
}
.bn-12__hint {
font-size: .78rem;
color: var(--sub);
max-width: 360px;
line-height: 1.5;
opacity: .85;
}
.bn-12__hint code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
padding: 1px 5px;
border-radius: 4px;
background: rgba(14,165,233,.14);
color: var(--sub);
}
@media (prefers-reduced-motion: reduce) {
.bn-12__badge {
animation: none!important;
transform: none!important;
}
}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: Floating Hovering Element
Source: https://codefronts.com/motion/css-bounce-animation/floating-hovering-element/
A soft, weightless bounce that loops indefinitely to make an illustration, badge, or icon look like it's floating in mid-air. Sky-blue soft pastel palette with a rounded card element that gently rises and falls in an infinite ease-in-out cycle. Perfect for hero illustrations, achievement badges, mascots, or any element that needs to feel "alive" without demanding attention. Uses alternate iteration so the up-and-down motion never resets — smooth continuous drift.
## HTML
```html
<div class="bn-12">
<p class="bn-12__label">Weightless float loop</p>
<div class="bn-12__badge" aria-hidden="true">
<svg viewBox="0 0 24 24" width="44" height="44" fill="currentColor" aria-hidden="true">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
</div>
<p class="bn-12__hint">The badge rises and falls infinitely with <code>alternate</code> iteration.</p>
</div>
```
## CSS
```css
.bn-12 {
--page-bg: #e0f2fe;
--surface: #ffffff;
--ink: #075985;
--sub: #0369a1;
--accent: #0ea5e9;
--accent-hi: #38bdf8;
--shadow: rgba(2,132,199,.35);
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: 24px;
padding: 32px;
background: radial-gradient(60% 60% at 50% 40%, rgba(14,165,233,.18) 0%, transparent 60%), var(--page-bg);
color: var(--ink);
text-align: center;
}
.bn-12 *,
.bn-12 *::before,
.bn-12 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-12 ::selection {
background: var(--accent);
color: #fff;
}
.bn-12__label {
font-size: .9rem;
font-weight: 600;
color: var(--sub);
letter-spacing: .02em;
}
.bn-12__badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 120px;
height: 120px;
border-radius: 32px;
background: linear-gradient(135deg,var(--accent-hi),var(--accent));
color: #fff;
box-shadow: 0 20px 40px -12px var(--shadow);
animation: bn-12-float 2.4s ease-in-out infinite alternate, bn-12-shadow 2.4s ease-in-out infinite alternate;
will-change: transform;
}
@keyframes bn-12-float {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-16px);
}
}
@keyframes bn-12-shadow {
0% {
box-shadow: 0 20px 40px -12px var(--shadow);
}
100% {
box-shadow: 0 32px 52px -14px var(--shadow);
}
}
.bn-12__hint {
font-size: .78rem;
color: var(--sub);
max-width: 360px;
line-height: 1.5;
opacity: .85;
}
.bn-12__hint code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
padding: 1px 5px;
border-radius: 4px;
background: rgba(14,165,233,.14);
color: var(--sub);
}
@media (prefers-reduced-motion: reduce) {
.bn-12__badge {
animation: none!important;
transform: none!important;
}
}
```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: Floating Hovering Element
Source: https://codefronts.com/motion/css-bounce-animation/floating-hovering-element/
A soft, weightless bounce that loops indefinitely to make an illustration, badge, or icon look like it's floating in mid-air. Sky-blue soft pastel palette with a rounded card element that gently rises and falls in an infinite ease-in-out cycle. Perfect for hero illustrations, achievement badges, mascots, or any element that needs to feel "alive" without demanding attention. Uses alternate iteration so the up-and-down motion never resets — smooth continuous drift.
## HTML
```html
<div class="bn-12">
<p class="bn-12__label">Weightless float loop</p>
<div class="bn-12__badge" aria-hidden="true">
<svg viewBox="0 0 24 24" width="44" height="44" fill="currentColor" aria-hidden="true">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
</div>
<p class="bn-12__hint">The badge rises and falls infinitely with <code>alternate</code> iteration.</p>
</div>
```
## CSS
```css
.bn-12 {
--page-bg: #e0f2fe;
--surface: #ffffff;
--ink: #075985;
--sub: #0369a1;
--accent: #0ea5e9;
--accent-hi: #38bdf8;
--shadow: rgba(2,132,199,.35);
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: 24px;
padding: 32px;
background: radial-gradient(60% 60% at 50% 40%, rgba(14,165,233,.18) 0%, transparent 60%), var(--page-bg);
color: var(--ink);
text-align: center;
}
.bn-12 *,
.bn-12 *::before,
.bn-12 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-12 ::selection {
background: var(--accent);
color: #fff;
}
.bn-12__label {
font-size: .9rem;
font-weight: 600;
color: var(--sub);
letter-spacing: .02em;
}
.bn-12__badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 120px;
height: 120px;
border-radius: 32px;
background: linear-gradient(135deg,var(--accent-hi),var(--accent));
color: #fff;
box-shadow: 0 20px 40px -12px var(--shadow);
animation: bn-12-float 2.4s ease-in-out infinite alternate, bn-12-shadow 2.4s ease-in-out infinite alternate;
will-change: transform;
}
@keyframes bn-12-float {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-16px);
}
}
@keyframes bn-12-shadow {
0% {
box-shadow: 0 20px 40px -12px var(--shadow);
}
100% {
box-shadow: 0 32px 52px -14px var(--shadow);
}
}
.bn-12__hint {
font-size: .78rem;
color: var(--sub);
max-width: 360px;
line-height: 1.5;
opacity: .85;
}
.bn-12__hint code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
padding: 1px 5px;
border-radius: 4px;
background: rgba(14,165,233,.14);
color: var(--sub);
}
@media (prefers-reduced-motion: reduce) {
.bn-12__badge {
animation: none!important;
transform: none!important;
}
}
```How this works
The element uses a simple 2-stop @keyframes bn-12-float: 0% translateY(0) → 100% translateY(-12px). The magic is the animation properties: animation: bn-12-float 2.4s ease-in-out infinite alternate. The alternate keyword is what makes it feel weightless — instead of jumping back to the start (which would cause a visible jerk), the animation reverses direction each cycle. So the element rises for 2.4s, then falls for 2.4s (playing the same keyframe backwards), then rises again. Total up-down cycle is 4.8s.
The ease-in-out timing function is critical — at the peak and trough, the motion slows to near-zero before reversing, mimicking gravity's momentary pause before pulling the element back down. A linear timing function would feel mechanical; ease-in-out feels physical.
Simultaneously, a soft box-shadow animates via @keyframes bn-12-shadow — the shadow moves LESS than the element (translateY -6px vs -12px on the element) and its blur increases as the element rises. This creates the illusion that the element is casting a shadow onto a floor below — as the element gets farther from the floor, its shadow becomes larger and softer.
The demo shows a floating badge with a star icon centered as the hero. The element occupies ~120×120px in the middle of a soft sky-blue background with subtle radial glow.
Make it yours
- Change float height by editing
translateY(-12px)— 6px for subtle drift, 20px for playful lift. Subtle drift is safer for portrait contexts (avatars, badges); playful lift is better for landing-page hero illustrations. - Change float speed by editing
animation-duration: 2.4s. Slower (3s+) feels more meditative; faster (1.5s) feels more energetic. Under 1s reads as "vibrating" not "floating." - Add subtle rotation for organic feel — combine
translateYwithrotate(-2deg)at 100%. Element tilts slightly as it rises. Great for hand-drawn illustration vibes. - For a triple-axis float (Y + X + rotate), use a multi-stop keyframe:
0% → 33% translateY(-8px) translateX(4px) → 66% translateY(-12px) translateX(-3px) → 100% translateY(0) rotate(0). Creates organic drift path. - For Tailwind:
animate-[bn12_2.4s_ease-in-out_infinite_alternate]with keyframes registered. See FAQ #4.
Gotchas — read before shipping
- The
alternatekeyword is what creates smooth weightless motion. Without it, the element jumps back totranslateY(0)at the end of each cycle which reads as "broken."alternatereverses the animation direction so the return is smooth. - Do NOT use
animation-fill-mode: forwardswith an infinite animation —forwardsis for one-shot animations that should hold their final state. Infinite loops don't need it. - The shadow scale must be LESS than the element scale — shadow moves half as far, blur increases as element rises. If shadow moves the same distance as the element, the element looks stuck to its shadow (not floating).
- For accessibility, this animation is PURELY DECORATIVE. It conveys nothing. Under
prefers-reduced-motion, disable entirely — show the element at its rest position. - For portraits or profile avatars, the float should be SUBTLE (6px max) — bigger floats make the person's face appear to bob distractingly. Reserve dramatic floats for illustrations without human subjects.
- For platforms with many floating elements (e.g., 20 badges on a page), stagger the animation delays randomly so they don't all sync up (which reads as "everything's connected" rather than "each element floats independently"). Use
animation-delay: calc(-1s * var(--i))with per-element index. - For
prefers-reduced-motion, disable entirely. The element rests attranslateY(0)with no shadow animation. Nothing meaningful is lost.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 45+ | 10+ | 40+ | 45+ |
transform + box-shadow + @keyframes. Universal, GPU-accelerated.