Loaders✨ Premium

Gradient Ring

Ring of 50 px — conic gradient transparent→#6366f1, centre masked by ::before at inset 5 px (fixed background #0a0a0f) — 1 rev/s linear, zero JS.

CSSGradient

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Loaders has 29 effects, including 4 free. Explore the category →

3 usage examples

Submit button while a form is being sent — Gradient Ring example 1

① Submit button while a form is being sent

WhenA contact or checkout form: the user clicks Submit, the button is disabled and the ring replaces the label for the duration of the network call.
WhyAt 24 px, the ring fits within the height of a standard button. Its fading arc (transparent to full colour) is less visually aggressive than a uniform-colour spinner and implies no percentage — suited when the duration is unknown.
Settingswidth: 24px; height: 24px, inset: 3px on ::before, background: #0a0a0f replaced with the button's background colour, animation-duration: 0.8s.
Music player — ring around the album art while the track buffers — Gradient Ring example 2

② Music player — ring around the album art while the track buffers

WhenAn audio player (app or "now playing" widget): the track starts on a slow connection, the player stays on screen and the ring spins around the round artwork while the buffer fills; it disappears as soon as playback resumes.
WhyThe centre mask is the whole point here: at 160 px the ring frames the artwork instead of covering it, and the gap-free conic gradient keeps that frame closed while still showing the direction of rotation clearly — a hard-segment spinner (fx-0421) would open a break in the frame, dots or a bar would need their own space. At this size the transparent tail lets the player surface show through: the ring reads as a halo orbiting the art, not a disc laid over the picture.
Settingswidth: 160px; height: 160px, inset: 6px on ::before (6 px ring, 148 px inner diameter), background: #0a0a0f replaced with the player card background (#12121a) — visible in the 4 px gap between the ring and the 140 px artwork laid on top —, animation-duration: 1.4s (at this diameter, 1 s feels rushed). The artwork is a sibling of the ring, not a child: it does not rotate with it.
Sign-in screen — "Verifying your credentials" on an otherwise empty screen — Gradient Ring example 3

③ Sign-in screen — "Verifying your credentials" on an otherwise empty screen

WhenInterstitial after a login form is submitted or on the way back from SSO: the page has nothing else to show until the session is open — the ring alone, centred, one status line, and nothing else.
WhyOn an empty screen the indicator carries the interface by itself, so it has to be big: the conic gradient is vector-drawn and stays crisp at 104 px, where a bitmap or dot loader degrades or scatters. Its smooth linear rotation suits a wait of unknown length that should feel reassuring — neither the pulse of "about to start" nor the false promise of a progress bar. And it is the only context on this page where the hard-coded mask (#0a0a0f) works untouched: the screen sits on the app's base surface.
Settingswidth: 104px; height: 104px, inset: 8px on ::before (8 px ring, more legible alone on a bare background), animation-duration: 1.2s; the ::before background: #0a0a0f is kept as is — the screen background is exactly #0a0a0f. role="status" + aria-label on the ring, aria-busy="true" on the screen.

How it works

Structure. A single div.loader-gradient-ring of 50 × 50 px, border-radius: 50%. Its background: conic-gradient(transparent, #6366f1) — the conic gradient sweeps 360° clockwise from 0° (top), from transparent to indigo, painting a disc whose colour gradually thickens. The ::before pseudo-element (content: ""; position: absolute; inset: 5px; background: #0a0a0f; border-radius: 50%) lays a 40 × 40 px disc over the centre, masking it: only the outer 5 px ring remains visible. No JavaScript, no dependencies.

Animation. @keyframes ringRotate { to { transform: rotate(360deg) } }, applied as animation: 1s linear 0s infinite. The disc spins at constant speed — one full revolution per second. The conic gradient rotates with it: the fully opaque zone leads, the transparent zone closes the circle. Measured in Chromium: the transform matrix confirms approximately 90° of rotation in 250 ms — a quarter turn per quarter second, infinite loop with no drift.

Integration constraint. The ::before background is hard-coded to #0a0a0f, the colour of the demo wrapper .demo-preview. Outside that wrapper, on any different background, the central mask stays dark and becomes visible: the ring then looks like a coloured disc with a dark centre rather than a transparent ring. To fix this, replace background: #0a0a0f on .loader-gradient-ring::before with the exact colour of the containing surface.

Accessibility

  • prefers-reduced-motion not handled: no @media (prefers-reduced-motion: reduce) rule in the sold code. Measured under reduced-motion emulation: playState: "running", the rotation continues. Add at minimum @media (prefers-reduced-motion: reduce) { .loader-gradient-ring { animation-duration: 10s } } to slow it considerably, or animation: none to stop it.
  • role="status" absent: the ring is silent for screen readers. For a loading indicator, set role="status" (or role="progressbar") on the element with aria-label="Loading…", or insert a visually hidden child text (.sr-only).
  • aria-busy / aria-live absent: the ring does not announce the end of loading. Best practice is to set aria-busy="true" on the container that is loading (not on the ring itself) and remove it once the content is ready. The ring remains a visual-only indicator.
  • If the ring is used as pure decoration with no semantic role, add aria-hidden="true" to div.loader-gradient-ring so assistive technologies ignore it — and delegate the loading notification to an existing aria-live region in the page.

Browser compatibility

Two properties set the floor: conic-gradient (Chrome 69, Firefox 83, Safari 12.1) and the inset shorthand (Chrome 87, Firefox 87, Safari 14.1 — more restrictive). Zero JavaScript, zero dependencies.

Chrome 87+✓ Full
Firefox 87+✓ Full
Safari 14.1+✓ Full
Edge 87+✓ Full
Chrome 69–86✓ Partial (inset ignored: centre unmasked, full disc visible)
Mobile iOS 14.5+✓ Full

Without conic-gradient (IE, browsers before 2018): transparent background, ring invisible. Without inset (Chrome 69–86, Firefox 61–86): the ::before mask does not position correctly, the conic gradient appears as a spinning full disc. In both cases, a classic border spinner is the natural fallback.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="loader-gradient-ring"></div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Outer diameter (CSS — width: 50px; height: 50px) 50 px Total ring size. Change both values together. 20–24 px: fits in a button or bubble. 60–80 px: zone-level indicator.
Ring thickness (CSS — inset: 5px on ::before) 5 px (inner diameter = 50 − 2×5 = 40 px) Gap between the outer edge and the centre mask. 3px: thin ring. 8px: thick ring, more legible at small sizes.
Mask background (CSS — background: #0a0a0f on ::before) #0a0a0f Must exactly match the container background. White: #ffffff. Shared CSS variable: var(--surface). Any mismatch makes the mask visible as a dark disc.
Arc colour (CSS — conic-gradient(transparent, #6366f1)) #6366f1 (indigo) Replace #6366f1 with any colour. The start remains transparent. For an arc that begins already coloured (no invisible zone): conic-gradient(#6366f199, #6366f1).
Rotation speed (CSS — first value of animation) 1s 0.6s: fast, urgent feel. 2s: slow, background-level indicator. Perceived speed also depends on arc length: a longer arc reads as faster.
Timing (CSS — linear) linear Standard for a spinner. ease-in-out gives a slight ease on each revolution — organic but unconventional for a loading indicator.
Rotation direction (CSS — animation-direction) normal (clockwise) animation-direction: reverse for counter-clockwise. Some interfaces use counter-clockwise for cancel or unload spinners.

FAQ

The ::before background is hard-coded to #0a0a0f — the demo wrapper colour. On any other background the mask stays dark and becomes visible. Fix: replace background: #0a0a0f on .loader-gradient-ring::before with your exact surface colour. If the ring appears on several different surfaces, a shared CSS variable (--ring-bg: your-colour) avoids editing multiple rules.
The current code (conic-gradient(transparent, #6366f1)) produces a full-circle fade: roughly a quarter of the ring is nearly invisible. For a solid arc over 270° followed by a 90° transparent tail, use conic-gradient(#6366f1 0deg 270deg, transparent 270deg 360deg). For hard edges with no fade: conic-gradient(#6366f1 270deg, transparent 270deg).
fx-0421 uses border: 4px solid with three indigo sides and one transparent side — a uniform ring with a clean gap, single HTML element, no mask. fx-0435 paints the gradient directly onto the disc background via conic-gradient and masks the centre with ::before: the colour varies continuously from transparent to full, with no gap. The border technique cannot produce an angular gradient; the conic-gradient technique supports any angular palette but requires the mask background to match the containing surface.