Workspace / component / backgrounds
Noise Light Background
paxfx add blocks/component-backgrounds-noise-lightlocal onlyTier: low. Reduced motion: Static grain frame.
"use client";
import type { ReactNode } from "react";
import { cn, useReducedMotion } from "@pax-sh/blocks";
export interface NoiseLightBackgroundProps {
/** 0 to 1; how visible the grain pass is. */
grain?: number;
/** 0 to 1; strength of the edge vignette. */
vignette?: number;
className?: string;
children?: ReactNode;
}
/**
* Bright surface with a film-grain noise pass and a faint vignette. The grain
* is an inline SVG turbulence tile shifted through a short stepped loop so it
* shimmers like film; only transform animates. Under reduced motion the grain
* holds a single static frame.
*/
export function NoiseLightBackground({
grain = 0.5,
vignette = 0.5,
className,
children,
}: NoiseLightBackgroundProps) {
const reduced = useReducedMotion();
const noiseTile =
"data:image/svg+xml;utf8," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.82" numOctaves="2" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter><rect width="180" height="180" filter="url(#n)" opacity="1"/></svg>',
);
return (
<div
className={cn(
"relative isolate overflow-hidden rounded-2xl border border-[var(--pax-border,rgba(18,17,15,0.12))]",
"bg-[var(--pax-paper,#f6f2ea)] text-[var(--pax-ink,#12110f)]",
className,
)}
data-pax-asset="component-backgrounds-noise-light"
>
<style>{`
@keyframes pax-noise-light-shimmer {
0% { transform: translate3d(0, 0, 0); }
25% { transform: translate3d(-34px, 18px, 0); }
50% { transform: translate3d(22px, -30px, 0); }
75% { transform: translate3d(-16px, -12px, 0); }
100% { transform: translate3d(0, 0, 0); }
}
`}</style>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0"
style={{
background: "linear-gradient(180deg, rgba(255,255,255,0.6) 0%, rgba(255,255,255,0) 45%)",
}}
/>
<div
aria-hidden="true"
className="pointer-events-none absolute -inset-10 mix-blend-multiply will-change-transform"
style={{
backgroundImage: `url("${noiseTile}")`,
backgroundSize: "180px 180px",
opacity: 0.24 * grain,
animation: reduced ? "none" : "pax-noise-light-shimmer 1.6s steps(4, jump-none) infinite",
}}
/>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0"
style={{
background: `radial-gradient(115% 90% at 50% 45%, transparent 62%, color-mix(in srgb, var(--pax-ink,#12110f) ${Math.round(
18 * vignette,
)}%, transparent) 100%)`,
}}
/>
<div className="relative z-[1]">{children}</div>
</div>
);
}