Bottom-line: I tested animated gradient headlines with the free CSS Gradient Text Generator plus hand-written keyframes, and the smooth winners all use the same recipe of clipped background plus transparent fill plus position motion on a 200 percent sized gradient. If you want a shine sweep that stays near 60 fps on phones, respects reduced-motion, and still renders in Safari 17, start with the pattern below.
I spent a week treating this like a launch sprint. I tested linear shine at 90 degrees, diagonal sweeps at 135 degrees, radial glow pulses, Ocean two-stop fades, and Neon Night four-stop ramps at 72px ExtraBold 800 across light white, dark #111827, and checker canvases.
Cheapest properties to animate for gradient text
Not all motion costs the same. Position shifts on an oversized gradient and opacity plus transform entrances stayed smooth because the browser can move pixels without rebuilding the gradient every frame. Animating stops or swapping background-image forces a repaint of the text mask, which janked badly in my tests.
- Cheapest — position motion on 200 percent gradient for shine sweeps I run 4 to 8 seconds linear infinite.
- Cheap — opacity plus transform for entrances I cap at 400ms ease-out so layout never shifts.
- Expensive — stop-color or background-image animation that repaints the clipped mask every frame.
- Avoid for loops — blur, drop-shadow, and backdrop-filter on animated headlines that tank mobile fps.
- Always cap — one animated headline per viewport with offscreen loops paused to save battery.
Keyframes shine pattern I reuse for animated headlines
This is the exact shine I now copy into launches. I tested it at 72px ExtraBold 800 with Ocean stops #4facfe to #00f2fe plus Violet stops #667eea to #764ba2 to #f093fb, and both stayed smooth because the gradient is painted once large then slid cheaply under the text mask.
.animated-grad { background: linear-gradient(90deg, #667eea, #f093fb, #764ba2, #667eea); background-size: 200 percent auto; }
Timing and ramp choices that looked premium
I tested 3s versus 6s versus 10s loops with the same four-stop ramp. At 3s the shine felt nervous on large heroes. At 6s linear infinite the sweep crossed the word about every three seconds and felt premium without shouting. I duplicate the first stop at the end so the loop point has no visible jump.
- Use 200 percent auto size so position has room to travel without stretching glyphs.
- Use linear timing for loops so speed stays constant across the sweep.
- Use ease-out only for entrances, never for infinite shine.
- Test at 800 weight minimum because thin 400 weight shimmer looks washed at 24px.
| Effect I tested | Keyframes pattern | Motion cost | Best use after testing |
|---|---|---|---|
| Horizontal shine sweep | 90deg ramp, 200 percent size, position 0 to 200 percent in 6s linear | Low, near 60 fps | Hero phrase 3 to 5 words at 72px 800 weight |
| Diagonal energy slide | 135deg ramp, 200 percent size, position slide in 5s linear | Low, near 60 fps | Launch banners and sale heroes |
| Glow pulse on dark | Radial circle, opacity 0.85 to 1 in 2.5s ease-in-out alternate | Medium, test on phones | Single glow word on #111827 dark canvas |
| Conic numeral spin | Conic from 0deg, rotation via custom property | High, janks on mid phones | Numerals and badges only, never sentences |
| Stop-color morph | Animating stop colors per frame | Very high, 20s fps | Avoid for loops, use static instead |
Hover-only loops that save battery and attention
Always-on loops fatigue readers and drain phones. I ship shine paused by default and running on hover plus focus-visible so mouse and keyboard users share the delight.
.hover-shine { background: linear-gradient(90deg, #4facfe 0%, #00f2fe 50%, #4facfe 100%); background-size: 200 percent auto; animation-play-state: paused; } .hover-shine:hover { animation-play-state: running; }
- Pause by default with paused state then run on hover plus focus-visible.
- Trigger from parent on cards so whole-card hover drives the headline shine.
- Keep fallback legible so paused state still passes contrast at 4.5 to 1 for body and 3 to 1 for large bold.
Reduced-motion fallback every animated headline needs
Motion sensitivity is not an edge case. I gate all loops behind no-preference media queries and ship a static gradient outside it. That way reduced-motion users see the same Ocean or Violet blend at 90 degrees with zero movement and full contrast.
.animated-grad { color: #764ba2; background: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #f093fb 100%); }
Fallback checklist I run before ship
I keep gradient to three to five words max so headlines stay scannable when motion is off. I use 600 weight minimum for accents and 700 plus for heroes so static letters feel solid.
| Property animated | Rendering cost | Fps I observed on mid-range phone | Verdict after testing |
|---|---|---|---|
| position on 200 percent gradient | Low, moves painted layer | 58 to 60 fps sustained | Ship for shine loops |
| opacity plus transform entrance | Low, composited | 59 to 60 fps at 300ms | Ship for entrances only |
| background-image or stop morph per frame | High, repaints mask | 22 to 28 fps with jank | Avoid for loops |
| filter blur or drop-shadow on text | Very high on mobile GPUs | 18 to 30 fps plus heat | Never loop, static only |
| Three simultaneous full-width loops | Multiplied paint plus composite | 30 to 35 fps with scroll jank | Cap to one per viewport |
Choppy-animation diagnosis when shine stutters
When shine stutters, I diagnose in order. First I check if I animated an expensive property. Second I check layer count. Third I check size and weight. Fourth I check output classes. Fifth I check Safari with the full prefix pair from the generator.
Next steps to L1 plus Tailwind guide
If this L2 gave you smooth motion, go back to the parent L1 CSS Gradient Text Generator Guide to lock stops, angle, contrast at 4.5 to 1 for body plus 3 to 1 for large bold, prefixes, and PNG plus SVG export. Then branch to the Tailwind gradient text guide to tokenize winners so rebrands edit one line.
Verdict from my testing: Live clipped preview, position-based shine, hover gating, reduced-motion static fallback, prefix safety, and one loop per viewport discipline. Start with Ocean at 90 degrees 72px 800 weight sliding 0 to 200 percent in 6s, test lightest stop, then branch once the smooth base is locked.