Home / Snippets / Animation & Motion /

Spin

Continuous 360° rotation — loading spinners in two lines of CSS.

Widely Supported
animationno-js

Quick implementation

@keyframes spin {
  to { transform: rotate(360deg); }
}

.spin {
  animation: spin 0.8s linear infinite;
}

/* Common spinner pattern */
.spinner {
  width: 2rem;
  height: 2rem;
  border: 3px solid oklch(0.30 0.02 260);
  border-top-color: oklch(0.72 0.19 265);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .spin, .spinner { animation-duration: 1.5s; }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building a loading indicator.

Goal: A CSS-only spinner using @keyframes rotate and a border trick — no JavaScript, no SVG.

Technical constraints:
- Use a single element with border: 3px solid + one transparent edge for the arc.
- Animation: rotate(360deg) with linear timing for constant speed.
- Use oklch() for all colors, not hex or rgba().
- Duration: 0.8s for a snappy feel.
- Include @media (prefers-reduced-motion: reduce) — slow the animation, don't remove it.

Framework variant (pick one):
A) Vanilla CSS class that works on any element.
B) React component — accept size and color props.

Edge cases to handle:
- Spinner must be centered in its container (use grid or flex parent).
- Reduced motion: slow to 1.5s instead of removing — users still need loading feedback.
- Size should be relative (rem) for accessibility zoom.

Return CSS.

Why this matters in 2026

Loading spinners are everywhere — buttons, page loads, data fetches. A CSS-only spinner needs no images, no SVG, no JavaScript animation frame. Two properties — a partial border and a rotation keyframe — create a universally recognized loading indicator.

The logic

The keyframe is minimal: to { transform: rotate(360deg); }. The browser infers from { transform: rotate(0deg); }. linear timing creates constant rotation speed — essential for spinners (easing would create an unnatural pause). The spinner appearance comes from a circle with one border color different from the others, creating a visible arc that rotates.

Accessibility & performance

For prefers-reduced-motion, slow the spinner rather than removing it — users still need loading feedback. A missing spinner is worse than a slow one. Add role="status" and aria-label="Loading" to the spinner element so screen readers announce the loading state. The rotation uses GPU compositing for zero-cost animation.