UtilitiesFree

Gradient Border Spin

Rainbow conic-gradient border that circles the box every 3 s, then every 1 s with a soft glow on hover, focus or tap.

CSS@propertyGradient

Updated

Hover or click the scene to interact

This effect is free — the Utilities category contains 43 effects total, including 3 free. Effect.Labs has 840 vanilla effects. Explore the category →

Usage examples

What would you like to know?

Compare two train faresSummarise a PDFPlan a weekend

① Question field of a research assistant

WhenOn the Yselvard assistant's home page, a single large pill-shaped “Ask your question…” field is ringed by the turning border; as soon as you click into it to type, it speeds up and glows.
WhyThe page has one action; the moving border points to it, and the speed-up while typing shows the field is active without adding any text.
Settings--gsp-w: min(460px, 100%); --gsp-h: 54px; --gsp-radius: 27px; --gsp-fill: #12111b; the box is a <form> with a real <input> and a button, without .gsp-link: :focus-within reacts to the field.
FollowingBrowse

Channels you follow · 2 live now

LIVE1.2k viewers
Ceramics workshop Crafts · Marion
Evening chess Offline · 3 h ago
Balcony garden Offline · yesterday
Piano at noon Offline · 2 days ago
LIVE312 viewers
Sunday cooking Food · Karim
Night sky Offline · 4 days ago

② “Live” thumbnails on a streaming site

WhenIn Ludrevane's list of followed channels, only the two channels streaming right now carry the border, thinner and slower; the other thumbnails stay plain.
WhyA turning border is spotted in a grid without reading labels: the eye finds what is happening now straight away.
SettingsOne root for the grid of six thumbnails; --gsp-w: 100%; --gsp-h: auto; --gsp-width: 2px; --gsp-radius: 9px; --gsp-speed: 4s; only live thumbnails have the gsp-box class and a .gsp-link link.

Bookshop giveaway · 6 entries

LMLéa MarchalEntered 2 Nov
HBHugo BernierEntered 3 Nov
ICInès CaradecEntered 3 Nov
SOSami OualiEntered 5 Nov
NPNora PelletEntered 6 Nov
AVArthur VasseEntered 7 Nov

③ Winning card in a prize draw

WhenIn the Fenoustrel giveaway tool, a click on “Draw the winner” moves the border from card to card, slowing down, then leaves it on the winner; the other cards fade and the name appears below.
WhyThe turning border marks the result and stretches the suspense of the draw; it moves by adding or removing a single class, with no recoding.
Settings--gsp-w: auto; --gsp-h: auto; --gsp-speed: 2.5s; --gsp-fill: #1a1630; the scene wiring puts the gsp-box class on one card at a time. A first draw starts by itself on load; under reduced motion it jumps straight to the winner.

How it works

Behind the .gsp-box (200 × 120 px, 12 px radius), the ::before pseudo-element sticks out 3 px on every side and fills with a conic-gradient of pink #ff0080, orange #ff8c00, turquoise #40e0d0, violet #7b2ff7, then pink again to close the circle. The ::after pseudo-element, in the background colour, covers the inside 3 px in from the edge: what remains is a 6 px colour band, half outside, half inside.

The gradient starts from an angle, --gsp-angle, declared with @property as a real <angle>, which the gspSpin animation takes from 0 to 360° in 3 s, continuously and at constant speed. On hover, or when something in the box has focus (:focus-within), the duration drops to 1 s and a 4 px blur turns the band into a glow. Changing the duration of a running animation re-times it: as you enter or leave, the colours jump to another angle before turning again.

The label is a link, .gsp-link, whose ::after covers the whole box: a click anywhere follows it, and this link is what makes the effect reachable by keyboard and finger. Replace its href="#gradient-spin" with your URL and its text with yours. The radius is adjustable (--gsp-radius): the band and the inner mask follow it, all the way to a pill or a circle.

Accessibility

  • Keyboard: Tab stops on the link; the border speeds up, gets its glow, and a white ring (--gsp-ring) appears 4 px beyond the band when focus comes from the keyboard. With a field in the box, as in the assistant example, typing is what triggers the speed-up. In Safari, Tab only moves through links if the browser's keyboard setting allows it (Option+Tab otherwise).
  • Touch: tapping the link triggers the fast state in Chromium, Firefox and WebKit (measured with a simulated touch screen); a box with no link or button would stay inert to a finger in WebKit.
  • Reduced motion: with prefers-reduced-motion: reduce, the rotation stops, the gradient stays visible (pink at the top), and the soft glow still appears on hover and focus.
  • Decoration: the border carries no information; if it signals a state (“live”, “winner”), say it in text too, like the labels in the examples.

Browser compatibility

Pure CSS, no JavaScript. Measured in Chromium 151, Firefox 153 and Playwright's WebKit 26.5: rotation, speed-up and glow on hover, Tab, touch, two independent copies, 390 px, and no infinite animation left under reduced motion. Minimum versions from @mdn/browser-compat-data: the rotation needs @property (Chrome 85, Firefox 128, Safari 16.4); the stretched link uses inset (Chrome 87).

Chrome 87+✓ Full (measured in version 151)
Firefox 128+✓ Full (measured in version 153); from 83 to 127, static gradient, glow on hover
Safari 16.4+✓ Full (measured in WebKit); from 14.1 to 16.3, static gradient
Edge 87+✓ Full (Chromium engine)
Safari iOS 16.4+✓ Full, fast state when the link is tapped (measured in touch WebKit)
Chrome Android 87+✓ Full (Chromium engine)

Without @property, the angle takes the fallback written in var(--gsp-angle, 0deg): the rainbow border shows, still, and the soft glow keeps working (checked by removing the @property rule in Chromium).

The code

Copy the two blocks (HTML and CSS) into your page. No JavaScript, no dependencies.

HTML
<div class="fx-0765-root">
  <div class="gsp-box">
    <a class="gsp-link" href="#gradient-spin">Gradient Spin</a>
  </div>
</div>
CSS
.fx-0765-root,
.fx-0765-root *,
.fx-0765-root *::before,
.fx-0765-root *::after {
  box-sizing: border-box
}

.fx-0765-root {
  --gsp-w: 200px;          /* box width */
  --gsp-h: 120px;          /* box height */
  --gsp-radius: 12px;      /* box corner radius (the border follows) */
  --gsp-width: 3px;        /* border thickness */
  --gsp-colors: #ff0080, #ff8c00, #40e0d0, #7b2ff7, #ff0080;
  --gsp-speed: 3s;         /* one turn at rest */
  --gsp-speed-hover: 1s;   /* one turn on hover, focus, tap */
  --gsp-halo: 4px;         /* halo blur on hover */
  --gsp-fill: #0a0a0f;     /* box fill */
  --gsp-ring: #fff;        /* keyboard focus ring */
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  position: relative;
  font-family: sans-serif;
}

@property --gsp-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.fx-0765-root .gsp-box {
  width: var(--gsp-w);
  max-width: 100%;
  height: var(--gsp-h);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.95rem;
  font-weight: 600;
  color: white;
  border-radius: var(--gsp-radius);
  position: relative;
  font-family: inherit;
  user-select: none;
  background: var(--gsp-fill);
  z-index: 1;
}

.fx-0765-root .gsp-box::before {
  content: '';
  position: absolute;
  inset: calc(var(--gsp-width) * -1);
  border-radius: calc(var(--gsp-radius) + var(--gsp-width));
  background: conic-gradient(from var(--gsp-angle, 0deg), var(--gsp-colors));
  z-index: -2;
  animation: gspSpin var(--gsp-speed) linear infinite;
}

.fx-0765-root .gsp-box::after {
  content: '';
  position: absolute;
  inset: var(--gsp-width);
  background: var(--gsp-fill);
  border-radius: max(0px, calc(var(--gsp-radius) - var(--gsp-width)));
  z-index: -1;
}

.fx-0765-root .gsp-box:hover::before,
.fx-0765-root .gsp-box:focus-within::before {
  animation-duration: var(--gsp-speed-hover);
  filter: blur(var(--gsp-halo));
}

/* Stretched link: the whole box is clickable, and keyboard focus
   or a tap on this link triggers the same state as hover. */
.fx-0765-root .gsp-link {
  color: inherit;
  text-decoration: none;
  outline: none;
}

.fx-0765-root .gsp-link::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--gsp-radius);
}

.fx-0765-root .gsp-link:focus-visible::after {
  outline: 2px solid var(--gsp-ring);
  outline-offset: calc(var(--gsp-width) + 4px);
}

@keyframes gspSpin {
  to {
    --gsp-angle: 360deg;
  }
}

@media (prefers-reduced-motion: reduce) {
  .fx-0765-root .gsp-box::before,
  .fx-0765-root .gsp-box:hover::before,
  .fx-0765-root .gsp-box:focus-within::before {
    animation: none;
  }
}
JavaScript

Pure CSS — no JavaScript needed.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
--gsp-colors #ff0080, #ff8c00, #40e0d0, #7b2ff7, #ff0080 List of gradient colours, as many as you like; repeat the first one at the end to avoid a seam.
--gsp-speed 3s Time for one turn at rest.
--gsp-speed-hover 1s Time for one turn on hover, focus and tap; use the same value as --gsp-speed to keep only the glow.
--gsp-halo 4px Blur of the band on hover; 0px removes the glow.
--gsp-width 3px How far the band reaches on each side of the edge: the visible band is twice that (6 px by default).
--gsp-radius 12px Box corner radius; band and mask follow. Half the height gives a pill.
--gsp-w and --gsp-h 200px and 120px Box size; auto lets the content decide, and the width never exceeds the container.
--gsp-fill #0a0a0f Background of the box and of the inner mask.
--gsp-ring #fff Colour of the keyboard focus ring.

FAQ

Why do the colours jump when hover starts and ends?

Hover changes the animation duration (3 s → 1 s) without restarting it: the browser keeps the elapsed time and recomputes where the turn stands, hence a jump in angle. If you prefer a change with no jump, remove animation-duration from the hover rule and keep only the glow (filter).

How do I ring a whole card, with a heading and text?

Put your content in .gsp-box, set --gsp-h: auto so the height follows the content, and give it some padding. If the card leads somewhere, keep a .gsp-link link inside: it makes the whole card clickable and triggers the speed-up from the keyboard.

Is it a problem to have several boxes on one page?

No: everything hangs off .fx-0765-root and the angle is not inherited, so each box turns on its own. A single root can even hold several .gsp-box elements, like the thumbnail grid in the streaming example.