Workspace / component / backgrounds
Aurora Background
paxfx add blocks/component-backgrounds-auroralocal onlyTier: medium. Reduced motion: Curtains hold a fixed pose.
"use client";
import type { ReactNode } from "react";
import { cn, useReducedMotion } from "@pax-sh/blocks";
export interface AuroraBackgroundProps {
/** 0 to 1; brightness of the light curtains. */
intensity?: number;
/** Multiplier on sway speed. 1 is the tuned default. */
speed?: number;
className?: string;
children?: ReactNode;
}
/**
* Vertical light curtains in teal and violet swaying against a night sky.
* Each curtain is a blurred gradient layer animating transform only on its
* own duration, so the volumetric read composites entirely on the GPU.
* Under reduced motion the curtains hold a fixed pose.
*/
export function AuroraBackground({
intensity = 0.8,
speed = 1,
className,
children,
}: AuroraBackgroundProps) {
const reduced = useReducedMotion();
const clamped = Math.min(1, Math.max(0, intensity));
const dur = (seconds: number) => `${seconds / Math.max(0.1, speed)}s`;
const curtain = (
left: string,
width: string,
background: string,
animation: string,
opacity: number,
) => (
<div
aria-hidden="true"
className="pointer-events-none absolute top-[-25%] h-[150%] blur-3xl will-change-transform"
style={{
left,
width,
background,
opacity: opacity * clamped,
mixBlendMode: "screen",
transform: "skewX(-8deg)",
animation: reduced ? "none" : animation,
}}
/>
);
return (
<div
className={cn(
"relative isolate overflow-hidden rounded-2xl border",
"border-[color-mix(in_srgb,var(--pax-paper,#f6f2ea)_14%,transparent)]",
"bg-[var(--pax-night,#0b0d10)] text-[var(--pax-paper,#f6f2ea)]",
className,
)}
data-pax-asset="component-backgrounds-aurora"
>
<style>{`
@keyframes pax-aurora-sway-a {
from { transform: skewX(-8deg) translate3d(-6%, -2%, 0); }
to { transform: skewX(-4deg) translate3d(7%, 2%, 0); }
}
@keyframes pax-aurora-sway-b {
from { transform: skewX(-10deg) translate3d(6%, 2%, 0); }
to { transform: skewX(-5deg) translate3d(-7%, -2%, 0); }
}
@keyframes pax-aurora-sway-c {
from { transform: skewX(-6deg) translate3d(-4%, 1%, 0); }
to { transform: skewX(-11deg) translate3d(5%, -2%, 0); }
}
`}</style>
{curtain(
"8%",
"26%",
"linear-gradient(to bottom, transparent 4%, rgba(45,212,191,0.5) 34%, rgba(45,212,191,0.14) 68%, transparent 96%)",
`pax-aurora-sway-a ${dur(22)} ease-in-out infinite alternate`,
0.9,
)}
{curtain(
"38%",
"30%",
"linear-gradient(to bottom, transparent 6%, rgba(139,92,246,0.48) 40%, rgba(139,92,246,0.12) 72%, transparent 98%)",
`pax-aurora-sway-b ${dur(31)} ease-in-out infinite alternate`,
0.85,
)}
{curtain(
"66%",
"24%",
"linear-gradient(to bottom, transparent 5%, rgba(45,212,191,0.34) 30%, rgba(139,92,246,0.24) 62%, transparent 95%)",
`pax-aurora-sway-c ${dur(27)} ease-in-out infinite alternate`,
0.8,
)}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0"
style={{
background: "linear-gradient(to top, var(--pax-night,#0b0d10) 0%, transparent 34%)",
}}
/>
<div className="relative z-[1]">{children}</div>
</div>
);
}