25 CSS Spinners02 / 25

Pure CSSMIT licensed

Dual Counter-Rotate Ring Spinner

Two concentric rings spin in opposite directions — outer purple and inner pink — with a pulsing radial core, creating a hypnotic, gyroscopic loading indicator.

Published

Live Demo
Try it

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

The code

<div class="sp-02">
  <div class="sp-02__wrap">
    <div class="sp-02__outer-track"></div>
    <div class="sp-02__outer"></div>
    <div class="sp-02__inner-track"></div>
    <div class="sp-02__inner"></div>
    <div class="sp-02__core"></div>
  </div>
</div>
.sp-02,
.sp-02 *,
.sp-02 *::before,
.sp-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sp-02 {
  --bg: #0d0017;
  --outer: #b44fff;
  --inner: #ff4fa3;
  --outer-track: rgba(180,79,255,0.15);
  --inner-track: rgba(255,79,163,0.15);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: var(--bg);
}

.sp-02__wrap {
  position: relative;
  width: 80px;
  height: 80px;
}

.sp-02__outer-track {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 4px solid var(--outer-track);
}

.sp-02__outer {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 4px solid transparent;
  border-top-color: var(--outer);
  border-bottom-color: var(--outer);
  animation: sp-02-cw 1.2s linear infinite;
  filter: drop-shadow(0 0 6px var(--outer));
}

.sp-02__inner-track {
  position: absolute;
  inset: 14px;
  border-radius: 50%;
  border: 3px solid var(--inner-track);
}

.sp-02__inner {
  position: absolute;
  inset: 14px;
  border-radius: 50%;
  border: 3px solid transparent;
  border-left-color: var(--inner);
  border-right-color: var(--inner);
  animation: sp-02-ccw 0.9s linear infinite;
  filter: drop-shadow(0 0 6px var(--inner));
}

.sp-02__core {
  position: absolute;
  inset: 32px;
  border-radius: 50%;
  background: radial-gradient(circle,rgba(180,79,255,0.6),transparent);
  animation: sp-02-pulse 1.2s ease-in-out infinite;
}

@keyframes sp-02-cw {
  to {
    transform: rotate(360deg);
  }
}

@keyframes sp-02-ccw {
  to {
    transform: rotate(-360deg);
  }
}

@keyframes sp-02-pulse {
  0%,
    100% {
    opacity: 0.4;
    transform: scale(0.8);
  }

  50% {
    opacity: 1;
    transform: scale(1.2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sp-02__outer,
    .sp-02__inner,
    .sp-02__core {
    animation: none;
  }
}
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 Spinner 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: Dual Counter-Rotate Ring Spinner
Source: https://codefronts.com/motion/css-spinner-designs/dual-counter-rotate-ring-spinner/

Two concentric rings spin in opposite directions — outer purple and inner pink — with a pulsing radial core, creating a hypnotic, gyroscopic loading indicator.
## HTML
```html
<div class="sp-02">
  <div class="sp-02__wrap">
    <div class="sp-02__outer-track"></div>
    <div class="sp-02__outer"></div>
    <div class="sp-02__inner-track"></div>
    <div class="sp-02__inner"></div>
    <div class="sp-02__core"></div>
  </div>
</div>
```
## CSS
```css
.sp-02,
.sp-02 *,
.sp-02 *::before,
.sp-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.sp-02 {
  --bg: #0d0017;
  --outer: #b44fff;
  --inner: #ff4fa3;
  --outer-track: rgba(180,79,255,0.15);
  --inner-track: rgba(255,79,163,0.15);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: var(--bg);
}

.sp-02__wrap {
  position: relative;
  width: 80px;
  height: 80px;
}

.sp-02__outer-track {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 4px solid var(--outer-track);
}

.sp-02__outer {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 4px solid transparent;
  border-top-color: var(--outer);
  border-bottom-color: var(--outer);
  animation: sp-02-cw 1.2s linear infinite;
  filter: drop-shadow(0 0 6px var(--outer));
}

.sp-02__inner-track {
  position: absolute;
  inset: 14px;
  border-radius: 50%;
  border: 3px solid var(--inner-track);
}

.sp-02__inner {
  position: absolute;
  inset: 14px;
  border-radius: 50%;
  border: 3px solid transparent;
  border-left-color: var(--inner);
  border-right-color: var(--inner);
  animation: sp-02-ccw 0.9s linear infinite;
  filter: drop-shadow(0 0 6px var(--inner));
}

.sp-02__core {
  position: absolute;
  inset: 32px;
  border-radius: 50%;
  background: radial-gradient(circle,rgba(180,79,255,0.6),transparent);
  animation: sp-02-pulse 1.2s ease-in-out infinite;
}

@keyframes sp-02-cw {
  to {
    transform: rotate(360deg);
  }
}

@keyframes sp-02-ccw {
  to {
    transform: rotate(-360deg);
  }
}

@keyframes sp-02-pulse {
  0%,
    100% {
    opacity: 0.4;
    transform: scale(0.8);
  }

  50% {
    opacity: 1;
    transform: scale(1.2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sp-02__outer,
    .sp-02__inner,
    .sp-02__core {
    animation: none;
  }
}
```

How this works

The outer ring is a full-width circle with border-top-color and border-bottom-color set and the sides transparent, animated with sp-02-cw (clockwise). The inner ring — inset by 14px — uses border-left-color and border-right-color and runs sp-02-ccw (counter-clockwise), creating a perpendicular axis visual at matching speed offset.

The centremost element animates opacity and scale via a radial-gradient background that pulses from 0.8 to 1.2 scale, adding depth. All three keyframes are independent so duration tweaks to any layer change the beat without affecting the others.

Make it yours

  • Swap --outer and --inner colours to any hue pair for brand theming.
  • Change the outer spin speed from 1.2s independently of the inner 0.9s to create different interference patterns.
  • Add a third outermost ring by duplicating the outer track/ring pair with inset:-12px for a triple-layer gyroscope.
  • Replace border-top/bottom with a full 4-side border and use clip-path:inset(0 50% 0 0) for a half-arc look.
  • Increase the core element to inset:20px for a larger glowing centre orb.

Gotchas — read before shipping

  • Two simultaneous infinite rotation animations share the GPU raster layer — test on low-end Android where this can cause jank at 60 Hz.
  • Counter-rotation with animation-direction:reverse on a sibling (not child) element means transforms do not compound — they are independent rotations from the same centre.
  • The core pulsing uses scale inside a @keyframes, which triggers layout in IE 11 (not an issue in modern browsers but worth noting for legacy audit trails).

Browser support

ChromeSafariFirefoxEdge
60+12+60+60+

No cutting-edge features; fully supported in all modern browsers.

Techniques used in this demo

Search CodeFronts

Loading…