16 CSS Pulse Animations02 / 16

Pure CSSMIT licensed

FAB Ripple Pulse

A floating action button (FAB) with an outward ripple pulse for mobile app dashboards — the sticky bottom-right "+" or "chat support" button that draws attention to the primary action hub. Warm off-white iOS consumer app palette with coral #ff6b6b FAB, mock dashboard content behind (feed cards, avatar bar), FAB positioned absolute; bottom: 24px; right: 24px. The ripple uses dual ::before and ::after pseudo-elements with staggered animation-delay for a continuous concentric outward wave.

Published

Live Demo
Try it

The code

<div class="pl-02">
  <div class="pl-02__header">
    <span class="pl-02__avatar" aria-hidden="true">A</span>
    <div class="pl-02__title">
      <strong>Today</strong>
      <span>Tue, Jul 8</span>
    </div>
    <span class="pl-02__bell" aria-hidden="true">🔔</span>
  </div>
  <div class="pl-02__feed">
    <div class="pl-02__card">
      <div class="pl-02__card-head">
        <span class="pl-02__dot pl-02__dot--pink" aria-hidden="true"></span>
        <span>Design review</span>
      </div>
      <p>Two comments on your mobile mockups — Sam and Priya.</p>
    </div>
    <div class="pl-02__card">
      <div class="pl-02__card-head">
        <span class="pl-02__dot pl-02__dot--amber" aria-hidden="true"></span>
        <span>Reminder</span>
      </div>
      <p>Standup at 10:00 · Zoom link auto-attached.</p>
    </div>
    <div class="pl-02__card">
      <div class="pl-02__card-head">
        <span class="pl-02__dot pl-02__dot--mint" aria-hidden="true"></span>
        <span>Task done</span>
      </div>
      <p>Onboarding flow shipped — 4 tickets closed.</p>
    </div>
  </div>
  <button class="pl-02__fab" type="button" aria-label="Create new post">
    <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
      <line x1="12" y1="5" x2="12" y2="19"/>
      <line x1="5" y1="12" x2="19" y2="12"/>
    </svg>
  </button>
</div>
.pl-02 {
  --bg: #faf7f0;
  --surface: #ffffff;
  --ink: #1c1917;
  --sub: #78716c;
  --accent: #ff6b6b;
  --accent-dark: #e05252;
  --line: #eee8dc;
  font-family: -apple-system,BlinkMacSystemFont,'SF Pro Display','Segoe UI',sans-serif;
  width: 100%;
  min-height: 100vh;
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 32px;
  background: var(--bg);
  color: var(--ink);
}

.pl-02 *,
.pl-02 *::before,
.pl-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.pl-02 ::selection {
  background: var(--accent);
  color: #fff;
}

.pl-02__header {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 4px 12px -6px rgba(28,25,23,.12);
  margin-bottom: 16px;
}

.pl-02__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: linear-gradient(135deg,#f472b6,#e879f9);
  color: #fff;
  font-size: .9rem;
  font-weight: 700;
}

.pl-02__title {
  flex: 1;
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}

.pl-02__title strong {
  font-size: 1rem;
  font-weight: 700;
}

.pl-02__title span {
  font-size: .75rem;
  color: var(--sub);
  margin-top: 1px;
}

.pl-02__bell {
  font-size: 18px;
}

.pl-02__feed {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}

.pl-02__card {
  padding: 14px 16px;
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 2px 8px -4px rgba(28,25,23,.08);
}

.pl-02__card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--sub);
  margin-bottom: 6px;
}

.pl-02__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}

.pl-02__dot--pink {
  background: #ec4899;
}

.pl-02__dot--amber {
  background: #f59e0b;
}

.pl-02__dot--mint {
  background: #10b981;
}

.pl-02__card p {
  font-size: .85rem;
  color: var(--ink);
  line-height: 1.45;
}
/* FAB — pinned bottom-right with ripple pulse */

.pl-02__fab {
  position: absolute;
  bottom: 24px;
  right: 24px;
  width: 56px;
  height: 56px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 8px 24px -6px rgba(255,107,107,.55),0 2px 4px rgba(28,25,23,.12);
  transition: transform .15s ease,background .18s ease;
  z-index: 2;
}

.pl-02__fab:hover {
  background: var(--accent-dark);
  transform: translateY(-1px);
}

.pl-02__fab:active {
  transform: translateY(1px);
}

.pl-02__fab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}

.pl-02__fab svg {
  position: relative;
  z-index: 1;
}
/* Ripple rings — two pseudo-elements, staggered animation-delay */

.pl-02__fab::before,
.pl-02__fab::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--accent);
  pointer-events: none;
  z-index: 0;
  animation: pl-02-ripple 1.8s ease-out infinite;
}

.pl-02__fab::after {
  animation-delay: .9s;
}

@keyframes pl-02-ripple {
  0% {
    transform: scale(1);
    opacity: .5;
  }

  70% {
    transform: scale(2);
    opacity: 0;
  }

  100% {
    transform: scale(2.2);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .pl-02__fab::before,
    .pl-02__fab::after {
    animation: 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 Pulse 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: FAB Ripple Pulse
Source: https://codefronts.com/motion/css-pulse-animation/fab-ripple-pulse/

A floating action button (FAB) with an outward ripple pulse for mobile app dashboards — the sticky bottom-right "+" or "chat support" button that draws attention to the primary action hub. Warm off-white iOS consumer app palette with coral #ff6b6b FAB, mock dashboard content behind (feed cards, avatar bar), FAB positioned absolute; bottom: 24px; right: 24px. The ripple uses dual ::before and ::after pseudo-elements with staggered animation-delay for a continuous concentric outward wave.
## HTML
```html
<div class="pl-02">
  <div class="pl-02__header">
    <span class="pl-02__avatar" aria-hidden="true">A</span>
    <div class="pl-02__title">
      <strong>Today</strong>
      <span>Tue, Jul 8</span>
    </div>
    <span class="pl-02__bell" aria-hidden="true">🔔</span>
  </div>
  <div class="pl-02__feed">
    <div class="pl-02__card">
      <div class="pl-02__card-head">
        <span class="pl-02__dot pl-02__dot--pink" aria-hidden="true"></span>
        <span>Design review</span>
      </div>
      <p>Two comments on your mobile mockups — Sam and Priya.</p>
    </div>
    <div class="pl-02__card">
      <div class="pl-02__card-head">
        <span class="pl-02__dot pl-02__dot--amber" aria-hidden="true"></span>
        <span>Reminder</span>
      </div>
      <p>Standup at 10:00 · Zoom link auto-attached.</p>
    </div>
    <div class="pl-02__card">
      <div class="pl-02__card-head">
        <span class="pl-02__dot pl-02__dot--mint" aria-hidden="true"></span>
        <span>Task done</span>
      </div>
      <p>Onboarding flow shipped — 4 tickets closed.</p>
    </div>
  </div>
  <button class="pl-02__fab" type="button" aria-label="Create new post">
    <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
      <line x1="12" y1="5" x2="12" y2="19"/>
      <line x1="5" y1="12" x2="19" y2="12"/>
    </svg>
  </button>
</div>
```
## CSS
```css
.pl-02 {
  --bg: #faf7f0;
  --surface: #ffffff;
  --ink: #1c1917;
  --sub: #78716c;
  --accent: #ff6b6b;
  --accent-dark: #e05252;
  --line: #eee8dc;
  font-family: -apple-system,BlinkMacSystemFont,'SF Pro Display','Segoe UI',sans-serif;
  width: 100%;
  min-height: 100vh;
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 32px;
  background: var(--bg);
  color: var(--ink);
}

.pl-02 *,
.pl-02 *::before,
.pl-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.pl-02 ::selection {
  background: var(--accent);
  color: #fff;
}

.pl-02__header {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 4px 12px -6px rgba(28,25,23,.12);
  margin-bottom: 16px;
}

.pl-02__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: linear-gradient(135deg,#f472b6,#e879f9);
  color: #fff;
  font-size: .9rem;
  font-weight: 700;
}

.pl-02__title {
  flex: 1;
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}

.pl-02__title strong {
  font-size: 1rem;
  font-weight: 700;
}

.pl-02__title span {
  font-size: .75rem;
  color: var(--sub);
  margin-top: 1px;
}

.pl-02__bell {
  font-size: 18px;
}

.pl-02__feed {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}

.pl-02__card {
  padding: 14px 16px;
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 2px 8px -4px rgba(28,25,23,.08);
}

.pl-02__card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--sub);
  margin-bottom: 6px;
}

.pl-02__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}

.pl-02__dot--pink {
  background: #ec4899;
}

.pl-02__dot--amber {
  background: #f59e0b;
}

.pl-02__dot--mint {
  background: #10b981;
}

.pl-02__card p {
  font-size: .85rem;
  color: var(--ink);
  line-height: 1.45;
}
/* FAB — pinned bottom-right with ripple pulse */

.pl-02__fab {
  position: absolute;
  bottom: 24px;
  right: 24px;
  width: 56px;
  height: 56px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 8px 24px -6px rgba(255,107,107,.55),0 2px 4px rgba(28,25,23,.12);
  transition: transform .15s ease,background .18s ease;
  z-index: 2;
}

.pl-02__fab:hover {
  background: var(--accent-dark);
  transform: translateY(-1px);
}

.pl-02__fab:active {
  transform: translateY(1px);
}

.pl-02__fab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}

.pl-02__fab svg {
  position: relative;
  z-index: 1;
}
/* Ripple rings — two pseudo-elements, staggered animation-delay */

.pl-02__fab::before,
.pl-02__fab::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--accent);
  pointer-events: none;
  z-index: 0;
  animation: pl-02-ripple 1.8s ease-out infinite;
}

.pl-02__fab::after {
  animation-delay: .9s;
}

@keyframes pl-02-ripple {
  0% {
    transform: scale(1);
    opacity: .5;
  }

  70% {
    transform: scale(2);
    opacity: 0;
  }

  100% {
    transform: scale(2.2);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .pl-02__fab::before,
    .pl-02__fab::after {
    animation: none!important;
  }
}
```

How this works

The FAB is a circular <button> element (56×56px per Material Design touch-target minimum) positioned absolutely inside the dashboard mock. Both ::before and ::after pseudo-elements share the same shape as the FAB (border-radius: 50%, positioned inset: 0), animated via a @keyframes pl-02-ripple that expands transform: scale(1) → scale(2.2) while fading opacity: .5 → 0. The two rings use animation-delay: 0s and animation-delay: .9s respectively, so as one ring finishes another begins — creating a continuous outward wave.

Why transform: scale here (not box-shadow like Demo #01): the FAB is a circle, and scaling a circular pseudo-element preserves the circle shape perfectly. box-shadow could also work, but transform: scale gives us the fading-outward wave feel (each ring is a full solid circle expanding + fading), which reads more like a physical ripple in water than a glow ring. Compositor-only, GPU-accelerated, 60fps safe.

Warm off-white surface (#faf7f0) with soft shadow depth — iOS consumer app convention (Cash App, Robinhood consumer, Instacart, DoorDash). Mock dashboard shows a feed of cards + top header bar with avatar so the FAB has real UI context, not floating in a void. Coral FAB (#ff6b6b) matches the warm palette while providing enough contrast to be the clear focal point.

The + icon inside the FAB is a pure SVG stroke — viewBox="0 0 24 24", two lines forming a cross, stroke-linecap: round for the friendly consumer feel. Icon has aria-hidden="true" and the button has aria-label="Create new post" for screen reader accessibility.

Make it yours

  • Change ripple speed by editing animation-duration: 1.8s — slower (2.4s) for ambient background attention, faster (1.2s) for more urgent action hub. The stagger delay should always be exactly HALF the duration for continuous wave feel.
  • Change ripple color by editing the background on ::before and ::after — match your brand primary. Keep the FAB fill and ripple color identical for visual coherence.
  • Add a third ring by adding a wrapper element with its own pseudo-element and animation-delay: 1.35s — creates a triple-cascade for extra visibility on cluttered dashboards.
  • Change FAB shape from circle to squircle by editing border-radius: 50% to border-radius: 20px — Material You / Android 12+ shape convention. Both pseudo-elements need matching border-radius.
  • For speed-dial menu FABs (with fan-out submenu), remove the ripple when the menu is open — the ripple competes with menu items for attention. Toggle via [aria-expanded="true"] &.pl-02__fab::before selector.

Gotchas — read before shipping

  • The FAB position absolute; bottom: 24px; right: 24px follows iOS Human Interface Guidelines — 24px inset from screen edges for thumb reachability. Do NOT reduce below 16px (thumb overshoots the edge) or increase above 32px (FAB feels stranded).
  • The 56×56px FAB size is Material Design's minimum touch target. Do NOT reduce below 48×48px (WCAG 2.5.5 minimum) — smaller FABs miss taps on real hardware, especially with thumb typing.
  • The ripple uses pointer-events: none on both pseudo-elements — otherwise the expanding rings would intercept clicks and block the button from receiving them.
  • For multi-FAB dashboards (chat + create + search), reserve ripple animation for the PRIMARY action only. Two rippling FABs on the same screen create attention competition and users don't know which is more important.
  • Auto-hiding the FAB on scroll-down (Material Design pattern) requires JS + scroll listener — but if you add it, keep the ripple animation running while hidden or the FAB pops in without the animation feeling continuous. Store animation-play-state on hide.
  • The FAB must have a proper aria-label ("Create new post", "Start chat", etc.) — a lone + icon means nothing to screen-reader users. Never skip this.

Browser support

ChromeSafariFirefoxEdge
45+10+40+45+

transform + opacity + @keyframes. Universal, GPU-accelerated.

Techniques used in this demo

Search CodeFronts

Loading…