Aurora Bars
Animated bars with aurora-inspired gradient effects and smooth transitions.
Installation
File Structure
Usage
import { AuroraBars } from "@/components/unlumen-ui/aurora-bars";
<AuroraBars />;API Reference
AuroraBars
barCount?number24Number of vertical bars rendered.
colors?string[]["#ffd6eb","#ff9acb","#ff5aa6","#ff2d78","#00000000"]Gradient color stops applied bottom → top.
maxHeightRatio?number0.92Maximum bar height as fraction of container (0–1).
minHeightRatio?number0.18Minimum bar height as fraction of container (0–1).
speed?number0.5Animation speed multiplier (higher = faster).
gap?number3Gap between bars in pixels.
blur?number0Blur applied to each bar in pixels (soft glow).
background?string"#000000"Background color behind all bars.
className?string—Additional className for the outer wrapper.
Notes
- Motion: the component uses
useAnimationFramefrommotion/reactand computes heights using a small procedural noise function (two sine waves blended with an arch envelope) so the bars undulate organically. - Gradient: colors are converted into a single
linear-gradient(to top, ...)string applied to each bar; supply any valid CSS color strings. - Performance: rendering is cheap — bars are simple divs with animated heights; prefer moderate
barCountvalues on low-end devices. - Visual tweaks:
bluradds a soft glow overlap, and a radial vignette darkens corners for depth.
Credits
Credit to @SannaGranqvistX for the original idea. The original is available for purchase on Framer
Keep in mind
Most components on this site are inspired by or recreated from existing work across the web. I'm not here to take credit; just to learn, experiment, and sometimes push things a bit further. If something looks familiar and I forgot to mention you, reach out and I'll fix that right away.
"use client";
import * as React from "react";
import { motion, useAnimationFrame } from "motion/react";
import { cn } from "@/lib/utils";
export interface AuroraBarsProps {
/** @default 24 */
barCount?: number;
/** gradient color stops, bottom to top — @default ["#ff2d78", "#c04aff", "#4a6fff", "#0a1aff", "#00000000"] */
colors?: string[];
/** max bar height as fraction of container height — @default 0.92 */
maxHeightRatio?: number;
/** min bar height as fraction of container height — @default 0.18 */
minHeightRatio?: number;
/** undulation speed — @default 0.5 */
speed?: number;
/** @default 3 */
gap?: number;
/** px blur per bar, creates soft glow — @default 0 */
blur?: number;
/** @default "#000000" */
background?: string;
className?: string;
}
/** two sine waves per bar for organic movement */
function barHeight(
index: number,
total: number,
time: number,
minH: number,
maxH: number,
): number {
// Arch envelope: tallest in the centre, shorter on edges
// arch envelope: tallest in centre, shorter on edges
const norm = index / (total - 1);
const arch = Math.sin(norm * Math.PI);
const phase1 = (index / total) * Math.PI * 2;
const phase2 = (index / total) * Math.PI * 5.3;
const wave =
0.5 +
0.25 * Math.sin(time * 1.1 + phase1) +
0.25 * Math.sin(time * 0.7 + phase2);
const blended = arch * 0.65 + wave * 0.35;
return minH + blended * (maxH - minH);
}
export function AuroraBars({
barCount = 24,
colors = ["#ffd6eb", "#ff9acb", "#ff5aa6", "#ff2d78", "#00000000"],
maxHeightRatio = 0.92,
minHeightRatio = 0.18,
speed = 0.5,
gap = 3,
blur = 0,
background = "#000000",
className,
}: AuroraBarsProps) {
const containerRef = React.useRef<HTMLDivElement>(null);
const [heights, setHeights] = React.useState<number[]>(() =>
Array.from({ length: barCount }, (_, i) =>
barHeight(i, barCount, 0, minHeightRatio, maxHeightRatio),
),
);
const timeRef = React.useRef(0);
useAnimationFrame((_, delta) => {
timeRef.current += (delta / 1000) * speed;
const t = timeRef.current;
setHeights(
Array.from({ length: barCount }, (_, i) =>
barHeight(i, barCount, t, minHeightRatio, maxHeightRatio),
),
);
});
const gradientStop = colors
.map((c, i) => `${c} ${Math.round((i / (colors.length - 1)) * 100)}%`)
.join(", ");
const gradient = `linear-gradient(to top, ${gradientStop})`;
return (
<div
ref={containerRef}
className={cn("relative w-full h-full overflow-hidden", className)}
style={{ background }}
>
<div className="absolute inset-0 flex items-end">
{Array.from({ length: barCount }).map((_, i) => {
const heightFraction = heights[i] ?? maxHeightRatio;
return (
<div
key={i}
className="flex-1"
style={{
height: "100%",
display: "flex",
alignItems: "flex-end",
padding: `0 ${gap / 2}px`,
}}
>
<motion.div
style={{
width: "100%",
height: `${heightFraction * 100}%`,
background: gradient,
borderRadius: "9999px 9999px 0 0",
filter: `blur(${blur}px)`,
opacity: 0.85,
}}
/>
</div>
);
})}
</div>
<div
className="absolute inset-0 pointer-events-none"
style={{
background:
"radial-gradient(ellipse 90% 80% at 50% 100%, transparent 40%, #000000cc 100%)",
}}
/>
</div>
);
}