February 25, 2026 · 8 min read
How to build a rotating conic-gradient button in vanilla CSS
A practical, code-first tutorial for creating a rotating conic-gradient button border in pure HTML and CSS.
This version is intentionally simple: one button, one conic-gradient border, and one animated custom property.
No JavaScript is required. The border rotates because --angle is registered as an animatable angle and fed into conic-gradient(from var(--angle, 0deg), ...).
Below is the exact setup with full copy-paste code, plus accessibility and motion fallback rules.
How the effect works
The button uses two backgrounds on the same element. A solid black background fills the inside, and a conic-gradient fills the border box.
When --angle rotates from 0deg to 360deg, the gradient appears to spin around the button while the content stays perfectly still.
- One semantic
<button>element. - Border created with
border: 2px solid transparentand layered backgrounds. - Gradient rotation controlled by
@property --angle. - Consistent loop with
animation: spin 2.5s linear infinite. - Accessible focus and reduced-motion fallback.
Step 1: HTML and base styles
Start with a plain button element and keep the styling minimal: black background, transparent border, rounded corners.
At this stage, there is no animation yet. We only establish shape, spacing, and typography.
HTML · html
<button class="btn-conic">Button</button>Base CSS · css
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
background: #262a34;
}
.btn-conic {
--fill: #3a3f50;
border-radius: 3rem;
padding: 1rem;
min-width: 200px;
background: #2c303d;
border: 2px solid transparent;
color: white;
cursor: pointer;
}Step 2: register and use --angle
@property tells the browser that --angle is an <angle> value, which allows smooth interpolation during animation.
Then we build the border using two background layers: linear-gradient(...) padding-box for the inner fill and conic-gradient(...) border-box for the animated ring.
Angle + border gradient · css
@property --angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
.btn-conic {
background:
linear-gradient(var(--fill), var(--fill)) padding-box,
conic-gradient(
from var(--angle, 0deg),
rgba(52, 168, 82, 0) 10deg,
rgba(52, 168, 82, 1) 38.9738deg,
rgba(255, 211, 20, 1) 62.3678deg,
rgba(255, 70, 65, 1) 87.0062deg,
rgba(49, 134, 255, 1) 107.428deg,
rgba(49, 134, 255, 0.5) 150deg,
rgba(49, 134, 255, 0) 200deg,
rgba(52, 168, 82, 0) 360deg
)
border-box;
animation: spin 2.5s linear infinite;
}- Keep
inherits: falsefor predictable behavior. - Use exact color stops if you want to match the visual feel.
- Use
lineartiming to keep angular speed constant.
Step 3: motion and accessibility polish
The animation itself is short: rotate the custom angle from 0deg to 360deg.
With this minimal version, we keep only the spin keyframes and no extra interaction styles.
Animation + a11y rules · css
@keyframes spin {
to {
--angle: 360deg;
}
}
.btn-conic:hover {
--fill: #424a5e;
}- Keep animation decorative; text should remain stable and readable.
- The entire effect lives in one button selector and one keyframes block.
- You can add focus and reduced-motion rules later if needed.
Complete copy-paste version
Use this full file as a baseline and then tune text, duration, and color stops to fit your product.
Full HTML + CSS · html
<!doctype html>
<html lang="en">
<body>
<button class="btn-conic">Button</button>
<style>
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
background: #262a34;
}
@property --angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
.btn-conic {
--fill: #3a3f50;
border-radius: 3rem;
padding: 1rem;
min-width: 200px;
background: #2c303d;
border: 2px solid transparent;
color: white;
cursor: pointer;
background:
linear-gradient(var(--fill), var(--fill)) padding-box,
conic-gradient(
from var(--angle, 0deg),
rgba(52, 168, 82, 0) 10deg,
rgba(52, 168, 82, 1) 38.9738deg,
rgba(255, 211, 20, 1) 62.3678deg,
rgba(255, 70, 65, 1) 87.0062deg,
rgba(49, 134, 255, 1) 107.428deg,
rgba(49, 134, 255, 0.5) 150deg,
rgba(49, 134, 255, 0) 200deg,
rgba(52, 168, 82, 0) 360deg
)
border-box;
animation: spin 2.5s linear infinite;
}
.btn-conic:hover {
--fill: #424a5e; /* hover bg */
}
@keyframes spin {
to {
--angle: 360deg;
}
}
</style>
</body>
</html>Final take
This pattern is effective because it stays lightweight: one element, one animated angle variable, and clean accessibility defaults.