More Animations Snippets
Aurora Text — Free HTML CSS Animated Gradient Text Snippet
Aurora Text · Animations · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Aurora Text — A Flowing Aurora Gradient Inside the Letters

Aurora text fills a headline with a slowly flowing, multi-color gradient — teal, green, and violet drifting through the letters like the northern lights — clipped to the glyph shapes so the color lives inside the type. This snippet builds it with pure CSS, plus a couple of optional JavaScript lines for accessibility and runtime recoloring.
A gradient clipped to the glyphs
The text color is set to transparent and filled with a linear-gradient via background-clip: text, so the gradient shows only through the letter shapes. The gradient repeats its aurora colors and is sized at 200% so there is room to move it around. Clipping to text means the color automatically follows the exact contours of the glyphs — including counters and serifs — across any number of lines, without masks or extra elements.
The flowing animation
The atFlow keyframe animates background-position from 0% 50% to 100% 50% and back on an 8-second ease-in-out loop. Because the gradient is larger than the text and its color stops repeat, sliding the position drifts the bands of color slowly through the letters, never quite repeating — the gentle, organic shimmer of an aurora. The ease-in-out (rather than linear) gives it a breathing, tidal quality instead of a constant scroll. A soft green drop-shadow adds an ambient glow around the type.
Why this looks like an aurora, not a rainbow
The palette and motion are deliberately restrained: cool teals and greens with a single violet accent, drifting slowly. That cool, limited palette plus the slow ease is what evokes the northern lights specifically, versus a fast, saturated rainbow which reads as generic. Swapping the colors changes the mood entirely while keeping the same mechanic.
Accessibility and runtime control
The animation is pure CSS, but the script checks prefers-reduced-motion and disables it for users who request less motion — the text still shows the gradient, just static. A tiny window.auroraText.colors([...]) helper rebuilds the flowing gradient from any list of CSS colors at runtime (it appends the first color to the end so the loop stays seamless), handy for theming or letting users pick a palette. Neither script is required for the core effect.
Performance
Only background-position animates, on a single element, which the browser composites cheaply — so even a giant headline shimmers smoothly. There is no canvas, no SVG, and no per-frame JavaScript.
Customizing it
Change the gradient colors and angle for a different aurora (or a warm sunset, or a metallic sheen), adjust the background-size and keyframe range to control how far the bands travel, retime the loop for a faster or calmer flow, or tune the drop-shadow glow. Provide a solid fallback color for contexts where background-clip: text is unsupported. Pair it with a retro grid or particle network backdrop for a striking hero.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA large headline fills with a flowing aurora gradient.
- 2Watch the flowBands of teal, green, and violet drift through the letters.
- 3Note the glowA soft drop-shadow halos the type.
- 4Recolor at runtimeCall window.auroraText.colors([...]) with your palette.
- 5Check reduced motionEnable the OS setting and the flow stops.
- 6Retime the loopAdjust the keyframe duration and gradient size.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The text color is set to transparent and filled with a linear-gradient via background-clip: text, so the gradient only shows through the glyph shapes. It automatically follows the exact contours of the letters, including counters, across multiple lines, with no masks or extra elements.
The gradient is sized at 200% so it is larger than the text, and the atFlow keyframe animates background-position back and forth on an 8-second ease-in-out loop. Because the color stops repeat and the gradient is oversized, the bands of color drift slowly through the letters without an obvious seam, giving the aurora shimmer.
The palette and motion are restrained: cool teals and greens with a single violet accent, drifting slowly on an ease-in-out curve. That limited, cool palette plus the tidal motion evokes the northern lights, whereas a fast, fully-saturated rainbow reads as a generic gradient. Swapping colors changes the mood while keeping the mechanic.
The script checks prefers-reduced-motion and disables the animation when set, so the text still shows the gradient but does not move for users who want less motion. Provide a solid fallback color too, since background-clip: text needs the text to be transparent and is unsupported in a few old contexts.
Make a small component that applies the class to its children, read prefers-reduced-motion in a mount effect to disable the animation when requested, and expose a colors prop that sets the gradient via inline style. The effect is pure CSS. In Tailwind, define the flow keyframe in the config and use bg-clip-text with text-transparent and an arbitrary gradient image.
Aurora Text — Export as HTML, React, Vue, Angular & Tailwind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Aurora Text</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#04040c;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;gap:16px;text-align:center;padding:24px}
.at-eyebrow{font-size:12px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#3f7a6b}
.at-text{font-size:clamp(56px,16vw,160px);font-weight:900;letter-spacing:-.04em;line-height:1;
background-image:linear-gradient(110deg,#22d3ee,#34d399,#a78bfa,#22d3ee,#34d399);
background-size:200% 200%;
-webkit-background-clip:text;background-clip:text;color:transparent;
animation:atFlow 8s ease-in-out infinite;
filter:drop-shadow(0 0 30px rgba(52,211,153,.25))}
@keyframes atFlow{
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
.at-sub{color:#7a8a92;font-size:15px;max-width:440px;line-height:1.55;margin-top:6px}
</style>
</head>
<body>
<div class="at-stage">
<p class="at-eyebrow">Northern lights</p>
<h1 class="at-text">Aurora</h1>
<p class="at-sub">A flowing aurora gradient drifts through the letters — clipped to the text, animated forever.</p>
</div>
<script>
// The aurora is pure CSS. JS only honours reduced-motion and exposes a tiny
// helper to recolour the gradient at runtime.
var el = document.querySelector('.at-text');
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
el.style.animation = 'none';
}
window.auroraText = {
colors: function (list) {
// list: array of CSS colors -> rebuild the flowing gradient
el.style.backgroundImage = 'linear-gradient(110deg,' + list.concat(list[0]).join(',') + ')';
}
};
</script>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Aurora Text</title>
<!-- Tailwind CSS v3+ — arbitrary value classes (e.g. bg-[#0f172a]) are valid -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
@keyframes atFlow{
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,-apple-system,sans-serif;background:#04040c;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;gap:16px;text-align:center;padding:24px
}
</style>
</head>
<body>
<div class="at-stage">
<p class="text-xs font-bold tracking-[.18em] uppercase text-[#3f7a6b]">Northern lights</p>
<h1 class="at-text text-[clamp(56px,16vw,160px)] font-black tracking-[-.04em] leading-none [background-image:linear-gradient(110deg,#22d3ee,#34d399,#a78bfa,#22d3ee,#34d399)] bg-[size:200%_200%] [-webkit-background-clip:text] bg-clip-text text-transparent [animation:atFlow_8s_ease-in-out_infinite] [filter:drop-shadow(0_0_30px_rgba(52,211,153,.25))]">Aurora</h1>
<p class="text-[#7a8a92] text-[15px] max-w-[440px] leading-[1.55] mt-1.5">A flowing aurora gradient drifts through the letters — clipped to the text, animated forever.</p>
</div>
<script>
// The aurora is pure CSS. JS only honours reduced-motion and exposes a tiny
// helper to recolour the gradient at runtime.
var el = document.querySelector('.at-text');
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
el.style.animation = 'none';
}
window.auroraText = {
colors: function (list) {
// list: array of CSS colors -> rebuild the flowing gradient
el.style.backgroundImage = 'linear-gradient(110deg,' + list.concat(list[0]).join(',') + ')';
}
};
</script>
</body>
</html>import React, { useEffect } from 'react';
// CSS — optionally move to AuroraText.module.css
const css = `
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#04040c;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;gap:16px;text-align:center;padding:24px}
.at-eyebrow{font-size:12px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#3f7a6b}
.at-text{font-size:clamp(56px,16vw,160px);font-weight:900;letter-spacing:-.04em;line-height:1;
background-image:linear-gradient(110deg,#22d3ee,#34d399,#a78bfa,#22d3ee,#34d399);
background-size:200% 200%;
-webkit-background-clip:text;background-clip:text;color:transparent;
animation:atFlow 8s ease-in-out infinite;
filter:drop-shadow(0 0 30px rgba(52,211,153,.25))}
@keyframes atFlow{
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
.at-sub{color:#7a8a92;font-size:15px;max-width:440px;line-height:1.55;margin-top:6px}
`;
export default function AuroraText() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
// The aurora is pure CSS. JS only honours reduced-motion and exposes a tiny
// helper to recolour the gradient at runtime.
var el = document.querySelector('.at-text');
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
el.style.animation = 'none';
}
window.auroraText = {
colors: function (list) {
// list: array of CSS colors -> rebuild the flowing gradient
el.style.backgroundImage = 'linear-gradient(110deg,' + list.concat(list[0]).join(',') + ')';
}
};
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
}, []);
return (
<>
<style>{css}</style>
<div className="at-stage">
<p className="at-eyebrow">Northern lights</p>
<h1 className="at-text">Aurora</h1>
<p className="at-sub">A flowing aurora gradient drifts through the letters — clipped to the text, animated forever.</p>
</div>
</>
);
}import React, { useEffect } from 'react';
// Requires Tailwind CSS v3+ — https://tailwindcss.com/docs/installation
// Arbitrary value classes (e.g. bg-[#0f172a]) are valid Tailwind v3+
export default function AuroraText() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
// The aurora is pure CSS. JS only honours reduced-motion and exposes a tiny
// helper to recolour the gradient at runtime.
var el = document.querySelector('.at-text');
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
el.style.animation = 'none';
}
window.auroraText = {
colors: function (list) {
// list: array of CSS colors -> rebuild the flowing gradient
el.style.backgroundImage = 'linear-gradient(110deg,' + list.concat(list[0]).join(',') + ')';
}
};
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
}, []);
return (
<>
<style>{`
@keyframes atFlow{
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,-apple-system,sans-serif;background:#04040c;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;gap:16px;text-align:center;padding:24px
}
`}</style>
<div className="at-stage">
<p className="text-xs font-bold tracking-[.18em] uppercase text-[#3f7a6b]">Northern lights</p>
<h1 className="at-text text-[clamp(56px,16vw,160px)] font-black tracking-[-.04em] leading-none [background-image:linear-gradient(110deg,#22d3ee,#34d399,#a78bfa,#22d3ee,#34d399)] bg-[size:200%_200%] [-webkit-background-clip:text] bg-clip-text text-transparent [animation:atFlow_8s_ease-in-out_infinite] [filter:drop-shadow(0_0_30px_rgba(52,211,153,.25))]">Aurora</h1>
<p className="text-[#7a8a92] text-[15px] max-w-[440px] leading-[1.55] mt-1.5">A flowing aurora gradient drifts through the letters — clipped to the text, animated forever.</p>
</div>
</>
);
}<template>
<div class="at-stage">
<p class="at-eyebrow">Northern lights</p>
<h1 class="at-text">Aurora</h1>
<p class="at-sub">A flowing aurora gradient drifts through the letters — clipped to the text, animated forever.</p>
</div>
</template>
<script setup>
import { onMounted } from 'vue';
let el;
onMounted(() => {
el = document.querySelector('.at-text');
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
el.style.animation = 'none';
}
window.auroraText = {
colors: function (list) {
// list: array of CSS colors -> rebuild the flowing gradient
el.style.backgroundImage = 'linear-gradient(110deg,' + list.concat(list[0]).join(',') + ')';
}
};
});
</script>
<style scoped>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#04040c;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;gap:16px;text-align:center;padding:24px}
.at-eyebrow{font-size:12px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#3f7a6b}
.at-text{font-size:clamp(56px,16vw,160px);font-weight:900;letter-spacing:-.04em;line-height:1;
background-image:linear-gradient(110deg,#22d3ee,#34d399,#a78bfa,#22d3ee,#34d399);
background-size:200% 200%;
-webkit-background-clip:text;background-clip:text;color:transparent;
animation:atFlow 8s ease-in-out infinite;
filter:drop-shadow(0 0 30px rgba(52,211,153,.25))}
@keyframes atFlow{
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
.at-sub{color:#7a8a92;font-size:15px;max-width:440px;line-height:1.55;margin-top:6px}
</style>// @ts-nocheck
// Note: vanilla JS DOM manipulation is preserved as-is inside ngAfterViewInit().
// For idiomatic Angular, replace document.getElementById() with @ViewChild() refs
// and move state into component properties with two-way binding.
import { Component, AfterViewInit, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-aurora-text',
standalone: true,
imports: [CommonModule],
encapsulation: ViewEncapsulation.None,
template: `
<div class="at-stage">
<p class="at-eyebrow">Northern lights</p>
<h1 class="at-text">Aurora</h1>
<p class="at-sub">A flowing aurora gradient drifts through the letters — clipped to the text, animated forever.</p>
</div>
`,
styles: [`
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#04040c;color:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;gap:16px;text-align:center;padding:24px}
.at-eyebrow{font-size:12px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#3f7a6b}
.at-text{font-size:clamp(56px,16vw,160px);font-weight:900;letter-spacing:-.04em;line-height:1;
background-image:linear-gradient(110deg,#22d3ee,#34d399,#a78bfa,#22d3ee,#34d399);
background-size:200% 200%;
-webkit-background-clip:text;background-clip:text;color:transparent;
animation:atFlow 8s ease-in-out infinite;
filter:drop-shadow(0 0 30px rgba(52,211,153,.25))}
@keyframes atFlow{
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
.at-sub{color:#7a8a92;font-size:15px;max-width:440px;line-height:1.55;margin-top:6px}
`]
})
export class AuroraTextComponent implements AfterViewInit {
ngAfterViewInit(): void {
// The aurora is pure CSS. JS only honours reduced-motion and exposes a tiny
// helper to recolour the gradient at runtime.
var el = document.querySelector('.at-text');
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
el.style.animation = 'none';
}
window.auroraText = {
colors: function (list) {
// list: array of CSS colors -> rebuild the flowing gradient
el.style.backgroundImage = 'linear-gradient(110deg,' + list.concat(list[0]).join(',') + ')';
}
};
}
}