← Back to gallery
CSS

Border-trick Ring Spinner

A square with border-radius: 50% turns into a ring; side-specific border colors do the rest. Three variants: a classic single-arc, a counter-rotating dual ring, and a two-tone two-halves loop.

spinnerloaderborder-trickborder-radiuspseudo-elementcounter-rotationprefers-reduced-motion

Spinner / border-trick / ring

Border-trick ring spinners

Reimplements the classic CSS spinner with a rounded square and side-specific border colors, then extends the primitive into a dual-ring counter-rotation and a two-tone halves variant.

Three transparent sides + one accent

Classic Border Spinner

A square with 50% border-radius becomes a ring. Coloring a single border side produces the classic quarter-segment spinner.

  • border-trick
  • one-accent
  • layout-safe

Two rings + opposite spin

Dual Ring

A pseudo-element ring sits inset from the outer ring and rotates the opposite way. The counter-rotation adds mechanical weight without moving the layout.

  • dual ring
  • counter-rotation
  • pseudo-element

Two colored halves + steady spin

Two-tone Halves

Two opposing border sides take contrasting hues. The ring reads as a full loop but the color split gives the rotation a visible reference point.

  • two-tone
  • directional cue
  • hero loader

Ring inspector

Classic Border Spinner

  • border-trick
  • one-accent
  • layout-safe

A square with 50% border-radius becomes a ring. Coloring a single border side produces the classic quarter-segment spinner. Transform-based rotation keeps the layout untouched while the visible segment sweeps. Thickness and accent come from border-width alone.

Helped you ship something? 🐟 Send my cat a churu

.border-ring {
  width: 86px;
  aspect-ratio: 1;
  border: 7px solid rgba(103, 232, 249, 0.18);
  border-top-color: rgba(103, 232, 249, 0.95);
  border-radius: 50%;
  animation: borderRingSpin 0.90s linear infinite;
}

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

@media (prefers-reduced-motion: reduce) {
  .border-ring { animation: none; }
}

How to make this

A CSS ring spinner uses a square box, equal border thickness, border-radius: 50%, and one accent border side rotated with transform.

html
1<div class="ring-spinner-status" role="status" aria-live="polite">  <span class="ring-spinner" aria-hidden="true"></span>  <span>Loading</span></div> <style>.ring-spinner-status {  display: inline-flex;  align-items: center;  gap: .75rem;  color: #cbd5e1;  font: 600 .95rem/1.2 ui-sans-serif, system-ui;}14.ring-spinner {  display: inline-block;  box-sizing: border-box;  inline-size: 3.25rem;18  aspect-ratio: 1;  border: .42rem solid rgba(103, 232, 249, .18);20  border-block-start-color: rgba(103, 232, 249, .96);  border-radius: 50%;22  animation: ring-spinner-recipe-spin .9s linear infinite;}@keyframes ring-spinner-recipe-spin {25  to { transform: rotate(1turn); }}@media (prefers-reduced-motion: reduce) {  .ring-spinner { animation: none; }}</style>

Annotated snippet

  1. Line 1The loading message is real status text. The spinner is decorative reinforcement, so assistive technology does not have to interpret a rotating border.
    PitfallShould a border spinner be announced to screen readers?

    Usually no. Mark the spinner aria-hidden and expose loading through adjacent text, aria-busy on the region, or a polite live region. The rotating border is not the semantic loading state.

  2. Line 14box-sizing: border-box keeps the outside diameter stable as border thickness changes. Without it, tuning the ring thickness changes the loader footprint.

    Same width: 54px and border: 7px on both. box-sizing: content-box adds the border outside the box, so the spinner balloons to ~68px outer diameter. box-sizing: border-box keeps the outer diameter at exactly 54px — the layout footprint stays stable as you tune the border thickness.

    PitfallWhy does my CSS border spinner wobble?

    The element is probably not square, border widths differ, or box-sizing is changing the outer size. Use aspect-ratio: 1, equal border thickness, and box-sizing: border-box.

  3. Line 18The square plus aspect-ratio: 1 is the geometry contract. A spinner that is not perfectly square will rotate as an oval and look off-center.
    PitfallWhy does my CSS border spinner wobble?

    The element is probably not square, border widths differ, or box-sizing is changing the outer size. Use aspect-ratio: 1, equal border thickness, and box-sizing: border-box.

  4. Line 20One border side gets the accent color while the other sides form the muted track. Rotation turns that single colored side into the moving head.

    A colored square border spins like a box; border-radius turns the same geometry into a circular loading ring.

    PitfallIs a border spinner more compatible than conic-gradient?

    Yes. Border, border-radius, and transform rotation are broadly supported. It is a good fallback when conic-gradient or masking is not available.

  5. Line 25border-radius: 50% converts the square border into a ring. This is the difference between border-trick spinner geometry and a rotating square outline.
  6. Line 22Transform rotation moves the already-painted ring without changing layout. Keep the timing linear so the spinner does not imply progress it cannot measure.
    PitfallAre border spinners performant?

    A small transform-rotated element is cheap. Problems come from rendering many indefinite spinners at once or adding heavy shadows and filters around each loader.

Other pitfalls

What should prefers-reduced-motion do for ring spinners?
Stop the infinite rotation but keep the static ring and loading text. Reduced motion should remove movement, not hide the pending state.

Notes

Overview

The classic CSS border-trick spinner: a square with border-radius: 50% becomes a ring, and giving one side a colored border (the other three transparent) produces a quarter-segment that rotates. Extends naturally into dual-ring counter-rotations and two-tone halves variants without adding any new mechanism — just more border manipulation.

When to use it

Reach for border-trick spinners when you need a spinner that works in legacy browsers without conic-gradient support (back to Chrome 4, Safari 5). Skip the dual-ring variant inside small UI — the inner counter-rotation is lost below ~24px diameter. Skip border-trick entirely if you have flexible color stops; conic-gradient produces tapered ends that read more refined.

How it works

Set the element to a fixed square size (e.g. 32px x 32px), apply border: 3px solid transparent; border-top-color: var(--accent); border-radius: 50% and rotate it with animation: spin 1s linear infinite. The result is a ring with a single visible quarter-arc rotating. For dual rings, nest a second spinner inside with the opposite animation-direction: reverse. For the two-tone halves variant, set two adjacent border sides to different colors so the rotating arc reads as complementary halves rather than a single tail.

Production gotchas

The border-trick spinner ages well in legacy browsers but cannot produce gradient or tapered tail ends; if your design needs those, reach for SVG or conic-gradient instead. Sub-pixel border widths produce jagged edges on non-retina displays — stick to integer pixel values. Animations stutter when the spinner is rendered inside an overflow: hidden ancestor with a transformed sibling because of compositor layer promotion races; add transform: translateZ(0) on the spinner to force its own layer.

Accessibility

Pair with role="status" + aria-label like the conic spinner. Under prefers-reduced-motion: reduce drop the animation entirely and replace with a static loading-shape SVG so the visual signal remains. The spinner color should hit at least 3:1 contrast against the background or it disappears for low-vision users.

References

Implementation depth

Border-ring spinners rely on simple geometry: a circular box, side-specific border colors, and rotation. Pseudo-elements can add counter-rotating layers without adding extra DOM.

Watch border thickness at small sizes. A ring that looks crisp at 48px can collapse at 16px, so test dense UI sizes and provide reduced motion that shows a static ring rather than a blank gap.