10 Inspirational CSS Animations from CodePen

Need fresh motion ideas for your next UI? This hands-on guide walks you through building a compact gallery of 10 inspirational CSS animations inspired by patterns you have probably seen on CodePen. You will learn how to assemble a single HTML page with a clean grid, then layer in modern, hardware-friendly animations that you can lift directly into your project.

Why 10 Inspirational CSS Animations from CodePen Matters

Animation sets tone, guides attention, and reduces cognitive load when used with purpose. A focused library of small, reusable CSS animations gives you a toolbox for loaders, hover feedback, progress hints, and micro-interactions without pulling in heavy JS. Most of the examples below rely on transforms, opacity, and background-position, which are well-accelerated across browsers. That mix keeps your UI responsive while still looking polished.

Prerequisites

You only need foundational CSS and a text editor. The patterns avoid complex frameworks and rely on pseudo-elements, custom properties, and semantic HTML.

  • Basic HTML
  • CSS custom properties
  • CSS pseudo-elements (::before / ::after)

Step 1: The HTML Structure

The gallery holds ten cards. Each card includes a title, a demo box, and a one-line description for quick scanning. The demo nodes carry a distinct class per animation, so you can apply targeted CSS and keep the markup tidy.

<!-- HTML -->
<main class="gallery">
  <article class="card">
    <h4 class="title">1. Gradient Spinner</h4>
    <div class="demo spinner" aria-hidden="true"></div>
    <p class="note">A rotating conic-gradient ring loader.</p>
  </article>

  <article class="card">
    <h4 class="title">2. Bouncing Ball</h4>
    <div class="demo ball" aria-hidden="true"></div>
    <p class="note">A squash-and-stretch bounce with a soft shadow.</p>
  </article>

  <article class="card">
    <h4 class="title">3. Typewriter</h4>
    <div class="demo typewriter" data-text="Hello, CSS Motion" role="img" aria-label="Typing Hello, CSS Motion"></div>
    <p class="note">Steps-based typing with a blinking caret.</p>
  </article>

  <article class="card">
    <h4 class="title">4. Morphing Blob</h4>
    <div class="demo blob" aria-hidden="true"></div>
    <p class="note">Organic border-radius morph for backgrounds or avatars.</p>
  </article>

  <article class="card">
    <h4 class="title">5. 3D Card Flip</h4>
    <div class="demo flip" aria-hidden="true"></div>
    <p class="note">Perspective flip that swaps front and back content.</p>
  </article>

  <article class="card">
    <h4 class="title">6. Pulse Ripple</h4>
    <div class="demo ripple" aria-hidden="true"></div>
    <p class="note">Two expanding rings fading out in sequence.</p>
  </article>

  <article class="card">
    <h4 class="title">7. Striped Loading Bar</h4>
    <div class="demo bar" aria-hidden="true"></div>
    <p class="note">Animated diagonal stripes sliding across a bar.</p>
  </article>

  <article class="card">
    <h4 class="title">8. Pendulum Swing</h4>
    <div class="demo pendulum" aria-hidden="true"></div>
    <p class="note">A weighted bob swinging from a fixed point.</p>
  </article>

  <article class="card">
    <h4 class="title">9. Breathing Triangle</h4>
    <div class="demo triangle" aria-hidden="true"></div>
    <p class="note">A pulsing pointer made from CSS borders.</p>
  </article>

  <article class="card">
    <h4 class="title">10. Wave Divider</h4>
    <div class="demo wave" aria-hidden="true"></div>
    <p class="note">A looping wave you can drop between sections.</p>
  </article>
</main>

Step 2: The Basic CSS & Styling

This base layer defines a dark theme, the grid, and card styling. Demos share a consistent size and rounded corners. Custom properties unify colors and animation timing, so you can reskin quickly.

/* CSS */
:root {
  --bg: #0f1117;
  --card: #151821;
  --muted: #9aa4b2;
  --text: #e8ecf1;
  --accent: #6ee7ff;
  --accent2: #a78bfa;
  --accent3: #22d3ee;
  --good: #34d399;
  --warn: #f59e0b;
  --danger: #f43f5e;
  --radius: 14px;
  --shadow: 0 6px 20px rgba(0,0,0,.35);
  --speed: 1.2s;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  background: radial-gradient(1200px 600px at 10% -20%, #1b2030 10%, transparent 50%),
              radial-gradient(1200px 600px at 90% 120%, #141824 10%, transparent 50%),
              var(--bg);
  color: var(--text);
  font: 500 16px/1.5 system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji", "Segoe UI Emoji";
  margin: 0;
}

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 22px;
  padding: 28px;
  max-width: 1200px;
  margin: 0 auto;
}

.card {
  background: linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.01));
  border: 1px solid rgba(255,255,255,.06);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
}

.title {
  margin: 6px 0 12px;
  font-size: 16px;
  color: #fff;
}

.demo {
  height: 160px;
  border-radius: 12px;
  background: #0f1220;
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
}

.note {
  color: var(--muted);
  font-size: 12px;
  margin: 12px 4px 4px;
}

Advanced Tip: Centralize motion in custom properties like –speed and derive durations from it. For example, set a faster hover tempo via calc(var(–speed) * 0.6) without editing multiple @keyframes blocks.

Step 3: Building Animations 1-5

The first set includes a spinner, a physics-style bounce, a typewriter, a morphing blob, and a 3D flip. These cover continuous loops, steps(), border-radius morphs, and 3D transforms with backface-visibility.

/* CSS */
/* 1) Gradient Spinner */
.spinner {
  --size: 72px;
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  background:
    conic-gradient(from 0turn, var(--accent), var(--accent2), var(--accent3), var(--accent));
  -webkit-mask: radial-gradient(circle 28px, transparent 29px, #000 30px);
          mask: radial-gradient(circle 28px, transparent 29px, #000 30px);
  animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(1turn); } }

/* 2) Bouncing Ball with shadow */
.ball {
  --size: 34px;
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #fff8, transparent 40%),
              radial-gradient(circle at 50% 60%, #fff2, transparent 50%),
              linear-gradient(135deg, var(--accent2), var(--accent));
  position: relative;
  animation: bounce 1s cubic-bezier(.3,.6,.3,1) infinite;
}
.ball::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -4px;
  width: 50px;
  height: 10px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, rgba(0,0,0,.4), transparent 60%);
  filter: blur(2px);
  animation: shadow 1s cubic-bezier(.3,.6,.3,1) infinite;
}
@keyframes bounce {
  0%, 100% { transform: translateY(0) scale(1,1); }
  50%      { transform: translateY(54px) scale(1.12,.88); }
}
@keyframes shadow {
  0%, 100% { transform: translateX(-50%) scaleX(1); opacity: .6; }
  50%      { transform: translateX(-50%) scaleX(1.25); opacity: .25; }
}

/* 3) Typewriter */
.typewriter {
  --text: attr(data-text);
  --chars: 18; /* match your string length */
  position: relative;
  width: calc(var(--chars) * 0.64ch);
  height: auto;
  padding: 10px 12px;
  background: #0b0e1a;
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06);
  color: #e6f3ff;
}
.typewriter::before {
  content: attr(data-text);
  white-space: nowrap;
  display: inline-block;
  overflow: hidden;
  border-right: 2px solid #e6f3ff;
  animation:
    typing steps(var(--chars)) 3s 0.2s both,
    caret 650ms steps(1) infinite;
}
@keyframes typing {
  0%   { width: 0; }
  100% { width: 100%; }
}
@keyframes caret {
  0%, 49%  { border-color: transparent; }
  50%,100% { border-color: #e6f3ff; }
}

/* 4) Morphing Blob */
.blob {
  --size: 86px;
  width: var(--size);
  height: var(--size);
  background: radial-gradient(circle at 30% 20%, #fff2, transparent 60%),
              linear-gradient(135deg, #f472b6, #60a5fa);
  animation: blob 8s ease-in-out infinite;
  filter: drop-shadow(0 12px 20px rgba(0,0,0,.25));
}
@keyframes blob {
  0%   { border-radius: 44% 56% 59% 41% / 44% 44% 56% 56%; transform: translate(0,0) rotate(0deg); }
  25%  { border-radius: 60% 40% 55% 45% / 40% 55% 45% 60%; transform: translate(6px,-4px) rotate(8deg); }
  50%  { border-radius: 40% 60% 42% 58% / 57% 43% 57% 43%; transform: translate(-4px,8px) rotate(-6deg); }
  75%  { border-radius: 58% 42% 60% 40% / 48% 60% 40% 52%; transform: translate(3px,-2px) rotate(4deg); }
  100% { border-radius: 44% 56% 59% 41% / 44% 44% 56% 56%; transform: translate(0,0) rotate(0deg); }
}

/* 5) 3D Card Flip (hover) */
.flip {
  perspective: 800px;
}
.flip::before,
.flip::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 10px;
  display: grid;
  place-items: center;
  backface-visibility: hidden;
  font: 600 16px/1 system-ui, sans-serif;
  letter-spacing: .4px;
}
.flip::before {
  background: linear-gradient(180deg, #1e2130, #171b28);
  color: #c7d2fe;
  content: "Front";
  transform: rotateY(0deg);
}
.flip::after {
  background: linear-gradient(180deg, #0f766e, #0b5f59);
  color: #e6fffb;
  content: "Back";
  transform: rotateY(180deg);
}
.flip:hover::before { animation: flipfwd .6s both; }
.flip:hover::after  { animation: flipback .6s both; }

@keyframes flipfwd {
  to { transform: rotateY(180deg); }
}
@keyframes flipback {
  to { transform: rotateY(360deg); }
}

How This Works (Code Breakdown)

The spinner uses a conic-gradient to sweep through colors and a mask to punch a hole, creating a ring. A one-turn rotation loop gives a classic loader without extra elements. Because rotation runs on the compositor, it stays smooth even under load.

The bouncing ball scales on the Y axis at the bottom of its path to simulate squash, while a blurred radial shadow scales wider mid-flight. The shadow is a pseudo-element, which keeps DOM lean.

The typewriter effect uses steps() animation against width, which reveals characters in fixed increments. The blinking caret reuses the right border and a short steps(1) loop. For exact alignment, the width uses ch units multiplied by the character count. If you love building circles and want to adapt the typewriter into a circular badge later, see the guide on how to make a circle with CSS.

The morphing blob relies on animated border-radius values in percentage pairs. Rotating a few degrees and nudging position introduces life-like drift. This pairs nicely with avatars or section accents.

The 3D card flip sets perspective on the container and uses backface-visibility to hide the back face until the rotation passes 90 degrees. The two faces live in ::before and ::after so the HTML stays clean.

Step 4: Building Animations 6-10

The second set rounds out the gallery with ripples, a striped bar, a pendulum, a breathing triangle, and a wave divider. These cover staggered pseudo-elements, repeating gradients, transform-origin tricks, border-based shapes, and animated backgrounds.

/* CSS */
/* 6) Pulse Ripple */
.ripple {
  --c: #38bdf8;
  width: 80px; height: 80px;
  border-radius: 50%;
  position: relative;
}
.ripple::before, .ripple::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--c);
  transform: scale(.2);
  opacity: .9;
  animation: ring 1.8s ease-out infinite;
}
.ripple::after { animation-delay: .9s; }
@keyframes ring {
  0%   { transform: scale(.2); opacity: .9; }
  80%  { transform: scale(1); opacity: 0; }
  100% { transform: scale(1); opacity: 0; }
}

/* 7) Striped Loading Bar */
.bar {
  width: 120px;
  height: 14px;
  border-radius: 999px;
  background:
    repeating-linear-gradient(
      45deg,
      rgba(255,255,255,.18) 0 10px,
      rgba(255,255,255,.05) 10px 20px
    );
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
  animation: slide 1.3s linear infinite;
}
@keyframes slide {
  from { background-position: 0 0; }
  to   { background-position: 40px 0; }
}

/* 8) Pendulum Swing */
.pendulum {
  width: 8px; height: 8px; /* anchor point */
  position: relative;
  transform-origin: top center;
  animation: swing 1.8s ease-in-out infinite;
}
.pendulum::before {
  content: "";
  position: absolute;
  left: 50%; top: 0;
  width: 2px; height: 70px;
  transform: translateX(-50%);
  background: linear-gradient(#2a2f42, #1b2135);
}
.pendulum::after {
  content: "";
  position: absolute;
  left: 50%; top: 70px;
  width: 28px; height: 28px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #fff6, transparent 60%),
              linear-gradient(135deg, #22d3ee, #818cf8);
  box-shadow: 0 10px 18px rgba(0,0,0,.35);
}
@keyframes swing {
  0%  { transform: rotate(-24deg); }
  50% { transform: rotate(24deg); }
  100%{ transform: rotate(-24deg); }
}

/* 9) Breathing Triangle (CSS borders) */
.triangle {
  --size: 0;
  width: 0; height: 0;
  border-left: 26px solid transparent;
  border-right: 26px solid transparent;
  border-bottom: 48px solid #fbbf24;
  filter: drop-shadow(0 10px 16px rgba(0,0,0,.35));
  animation: breathe 1.6s ease-in-out infinite;
}
@keyframes breathe {
  0%,100% { transform: translateY(0) scale(1); }
  50%     { transform: translateY(4px) scale(1.06); }
}

/* 10) Wave Divider */
.wave {
  --w: 220px;
  width: 100%;
  height: 100%;
  background:
    radial-gradient(50px 20px at 10% 100%,
      rgba(34,211,238,.6) 30%, transparent 31%) repeat-x,
    radial-gradient(50px 20px at 40% 100%,
      rgba(167,139,250,.6) 30%, transparent 31%) repeat-x,
    linear-gradient(#0b0e1a, #0b0e1a);
  background-size: 120px 60px, 120px 60px, auto;
  background-position: 0 110%, 60px 100%, 0 0;
  animation: waveh 4s linear infinite;
}
@keyframes waveh {
  from { background-position: 0 110%, 60px 100%, 0 0; }
  to   { background-position: 120px 110%, 180px 100%, 0 0; }
}

How This Works (Code Breakdown)

The ripple uses two pseudo-elements that scale from 0.2 to 1 and fade out. The staggered delay creates alternating rings without extra markup. Because only transform and opacity animate, it stays smooth.

The striped bar relies on repeating-linear-gradient for the diagonal pattern. Moving background-position creates the sliding illusion and avoids layout changes.

The pendulum sets transform-origin to the anchor at the top of the string. The bob lives in ::after at the end of the string, so a single rotate swings the entire assembly.

The triangle is made from borders, a classic trick that keeps markup minimal. If you want a refresher, see how to build the shape from scratch: how to make a triangle up with CSS. You can swap border-bottom color to match your theme and still get a crisp, scalable pointer.

The wave divider layers two radial gradients offset from each other, then scrolls them horizontally. If you prefer a full section divider with smooth curves, the tutorial on the CSS wave page divider shows another path using clip-path and larger amplitudes.

Advanced Techniques: Adding Animations & Hover Effects

Now wire up a few enhancements: make durations themeable, add hover pauses for inspection, and expose color theming at the component level. This snippet shows how to layer behavior without touching each animation block.

/* CSS */
/* Theming and control via data attributes */
.gallery {
  --speed: 1.2s;
}
.gallery[data-speed="fast"] { --speed: .8s; }
.gallery[data-speed="slow"] { --speed: 1.6s; }

/* Apply speed to candidates */
.spinner { animation-duration: calc(var(--speed) * .8); }
.ball, .ball::after { animation-duration: var(--speed); }
.ripple::before, .ripple::after { animation-duration: calc(var(--speed) * 1.5); }
.bar { animation-duration: calc(var(--speed) * 1.1); }
.pendulum { animation-duration: calc(var(--speed) * 1.5); }

/* Hover to pause for inspection */
.demo:hover {
  animation-play-state: paused;
}
.demo:hover::before,
.demo:hover::after {
  animation-play-state: paused;
}

/* Per-card accent override */
.card[data-accent="teal"] .ripple { --c: #2dd4bf; }
.card[data-accent="rose"] .ripple { --c: #fb7185; }

The data attributes on the gallery let you slow down or speed up the entire grid by changing one token. Using animation-play-state on hover helps designers audit intermediate frames. Per-card accents work well for ripples and loaders that need brand colors.

Accessibility & Performance

Motion should support comprehension without distracting users or making content harder to use. Keep loops purposeful and provide affordances that help users who prefer less motion or who navigate with assistive tech.

Accessibility

Mark decorative demos as aria-hidden to remove noise from the accessibility tree. When animation conveys meaning, provide a clear label, like the aria-label on the typewriter. Respect reduced motion by wrapping animation rules in a feature query that stops transitions and loops when the user sets that preference.

/* CSS */
/* Respect user motion settings */
@media (prefers-reduced-motion: reduce) {
  .demo, .demo::before, .demo::after {
    animation: none !important;
    transition: none !important;
  }
}

Offer a manual pause by binding a toggle that sets a data-paused attribute on the gallery and pauses animations. That pattern mirrors the hover-based pause but gives keyboard users the same control.

Performance

Favor transform and opacity. Those properties animate on the compositor and avoid layout thrash. Refrain from animating width, height, top, or left when a transform can do the job. Repeating gradients and background-position are cheap to animate and avoid DOM churn. Pseudo-elements keep markup small, which helps layout and reduces memory footprint. If you need shapes like triangles or circles frequently across your UI, use minimal CSS shape techniques and keep those render paths cheap. You can revisit how to make a circle with CSS to swap box shadows for gradients when performance matters.

Ship Motion That Serves the Interface

You now have a compact library of ten animations that cover spinners, loaders, micro-interactions, and section dividers. Each example stays close to GPU-friendly properties and uses pseudo-elements to reduce DOM weight. Mix and match these patterns to match your brand and layout, then evolve them into a system you can reuse across projects.

Leave a Comment