16 CSS Bounce Animations12 / 16

Pure CSSMIT licensed

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

Live Demo
Try it

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>
.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;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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 translateY with rotate(-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 alternate keyword is what creates smooth weightless motion. Without it, the element jumps back to translateY(0) at the end of each cycle which reads as "broken." alternate reverses the animation direction so the return is smooth.
  • Do NOT use animation-fill-mode: forwards with an infinite animation — forwards is 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 at translateY(0) with no shadow animation. Nothing meaningful is lost.

Browser support

ChromeSafariFirefoxEdge
45+10+40+45+

transform + box-shadow + @keyframes. Universal, GPU-accelerated.

Techniques used in this demo

Search CodeFronts

Loading…