The modern technique featured in Ania Kubów's video: smooth GPU-interpolated conic gradient rotations without DOM bloat or clumsy wrapper transforms.
Animated via CSS custom angle property. GPU-rendered 60FPS rotation without parent layout recalculations.
Historically in CSS, you could NOT animate gradients directly. Gradients are parsed as background images, and browsers have no mathematical way to interpolate between two static image descriptions.
::before element (e.g. 200% width/height), put a gradient on it, rotate the entire element with transform: rotate(360deg), and clip it with overflow: hidden. This caused clipping bugs, subpixel anti-aliasing artifacts, and extra layer compositing overhead.
The Modern Solution (CSS @property):
By registering --a as a typed variable with syntax: "<angle>", the browser's CSS parser recognizes it as a degree value (e.g. 0deg to 360deg). The browser can now natively interpolate the angle smoothly inside conic-gradient(from var(--a), ...)!
| Browser | Status | Version |
|---|---|---|
| Google Chrome | Supported | v85+ (2020) |
| Microsoft Edge | Supported | v85+ (2020) |
| Apple Safari | Supported | v16.4+ (Mar 2023) |
| Mozilla Firefox | Supported | v128+ (Jul 2024) |
| Baseline | Widely Available | Baseline 2024 |
As of July 2024 (Firefox 128 release), CSS @property is officially part of Baseline 2024 (Widely Available) across all major evergreen browsers worldwide.
/* 1. Register the custom angle variable with CSS Houdini */
@property --a {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
/* 2. Keyframes interpolate the angle mathematically */
@keyframes spin {
to {
--a: 360deg;
}
}
/* 3. Card Container */
.card {
position: relative;
border-radius: 20px;
}
/* 4. Ambient Aura Glow behind the card */
.card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
background: conic-gradient(from var(--a), #ff2a5f, #00f2fe, #7000ff, #ff2a5f);
animation: spin 3s linear infinite;
filter: blur(24px);
opacity: 0.8;
z-index: 0;
}
/* 5. Crisp Rotating Border */
.card::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
background: conic-gradient(from var(--a), #ff2a5f, #00f2fe, #7000ff, #ff2a5f);
animation: spin 3s linear infinite;
z-index: 1;
}
/* 6. Card Interior (insets by border width, covering center) */
.card-content {
position: absolute;
inset: 2px; /* border thickness */
background: #111726;
border-radius: calc(20px - 2px);
z-index: 2;
}