Original by Plinth · MIT
Backgrounds
Grain Gradient
A drifting four-colour mesh under real generated film grain — no noise texture to ship, no repeat seam.
#background#gradient#grain#noise#svg-filter
Install
npx shadcn@latest add https://plinthui.com/r/grain-gradient.json// No hooks or handlers here, so this stays a server component — the whole
// thing is CSS and one SVG filter. The filter, blend mode and keyframes are
// outside what Tailwind can express, so they live in the style element below.
const css = `
.grain {
position: relative;
min-height: 300px;
overflow: hidden;
background: #0a0912;
/* isolate, or mix-blend-mode on the grain reaches past this component and
composites against whatever is behind it on the page. */
isolation: isolate;
}
/* Four blobs drifting at different periods. Oversized and blurred hard, so
what you see is the overlap rather than four distinct shapes. */
.grain__mesh {
position: absolute;
inset: -25%;
filter: blur(58px);
background:
radial-gradient(28% 34% at 26% 28%, #ff7a4d 0%, transparent 70%),
radial-gradient(30% 32% at 74% 24%, #7d5cff 0%, transparent 70%),
radial-gradient(32% 36% at 68% 76%, #16b3c4 0%, transparent 70%),
radial-gradient(26% 30% at 24% 74%, #e5397e 0%, transparent 70%);
animation: grain-drift 22s ease-in-out infinite alternate;
}
.grain__noise {
position: absolute;
inset: 0;
filter: url(#grain-noise);
opacity: 0.42;
/* overlay rather than a flat alpha: grain should darken the lights and lift
the shadows, not fog the whole frame evenly. */
mix-blend-mode: overlay;
pointer-events: none;
}
.grain__def { position: absolute; width: 0; height: 0; }
@keyframes grain-drift {
0% { transform: translate3d(-3%, -2%, 0) scale(1.06); }
50% { transform: translate3d(2%, 3%, 0) scale(1.14); }
100% { transform: translate3d(3%, -3%, 0) scale(1.04); }
}
@media (prefers-reduced-motion: reduce) {
.grain__mesh { animation: none; }
}
`;
export function GrainGradient() {
return (
<>
<style>{css}</style>
<div className="grain">
<div className="grain__mesh" />
<div className="grain__noise" />
<svg className="grain__def" aria-hidden="true">
{/* Generated noise rather than a tiled PNG: no asset to ship, and it
never shows a repeat seam at any size. */}
<filter id="grain-noise">
<feTurbulence
type="fractalNoise"
baseFrequency="0.82"
numOctaves={3}
stitchTiles="stitch"
/>
<feColorMatrix type="saturate" values="0" />
</filter>
</svg>
</div>
</>
);
}