Aurora Background
BackgroundsAmbient gradient field: three soft colour fields drifting at three coprime speeds, a slow hue rotation so the colours themselves move, and a film-grain layer stepped at 12 fps.
Film grain over a gradient or image, blended and flickering at 12 fps. One SVG turbulence tile as a background whose translate steps; pure CSS, no canvas. Fills its positioned parent.
Grain.
Live, from the library itself: scroll, hover, navigate away and back. The demo is never gated.
Adjust · props are custom properties, set inline, so you edit the running instance
JavaScript of its own
0 B
CSS 4.9 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
every browser.
One command adds the integration, the base tokens and every component. Then import what you use.
npx astro add moonarcpnpm astro add moonarcbunx astro add moonarc---
import Noise from '@moonarc/core/Noise';
---
<section class="relative">
<Aurora />
<Noise opacity={0.1} />
<h2 class="relative">Grain over aurora</h2>
</section>Owns the file, no dependency. The registry item also installs the base tokens.
npx shadcn@latest add https://moonarc.dev/r/noise.jsonThe CLI needs a components.json and the @/* alias, which Setup has. The file lands in src/components/moonarc/.
The whole component. Self-contained styles in a cascade layer so your classes always win; it imports nothing.
---
/**
* Noise — film grain over whatever is beneath. Zero JS: one SVG turbulence
* tile as a background, blended, with its translate stepped at 12 fps so
* the grain lives without repainting. Fills its positioned parent and clips
* the moving grain to it, so it never widens the page.
*/
import type { HTMLAttributes } from 'astro/types';
interface Props extends HTMLAttributes<'div'> {
/** Strength, 0–1. */
opacity?: number;
/** Flicker the grain. */
animate?: boolean;
/** Blend mode against the layers beneath. */
blend?: 'overlay' | 'soft-light' | 'multiply' | 'screen' | 'normal';
/** Grain size: tile size in px. Smaller is finer. */
size?: number;
}
const { opacity = 0.08, animate = true, blend = 'overlay', size = 200, class: className, style, ...rest } = Astro.props;
const vars = [`--ma-noise-o:${opacity}`, `--ma-noise-blend:${blend}`, `--ma-noise-size:${size}px`, typeof style === 'string' ? style : ''].filter(Boolean).join(';');
---
<div class:list={['ma-noise', className]} aria-hidden="true" data-animate={animate ? '' : undefined} style={vars} {...rest}></div>
<style is:global>
@layer components {
/* the box is the parent's size and clips; the grain is a pseudo-element 10% larger on every side, so its translate
never shows an edge. The oversized box itself used to be the grain, and it widened any page it touched the edge of */
:where(.ma-noise) {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
mix-blend-mode: var(--ma-noise-blend, overlay);
opacity: var(--ma-noise-o, 0.08);
}
:where(.ma-noise)::before {
content: '';
position: absolute;
inset: -10%;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
background-size: var(--ma-noise-size, 200px) var(--ma-noise-size, 200px);
}
:where(.ma-noise[data-animate])::before {
animation: ma-noise 0.5s steps(6) infinite;
}
@media (prefers-reduced-motion: reduce) {
:where(.ma-noise)::before {
animation: none;
}
}
}
@keyframes ma-noise {
to {
translate: 5% 3%;
}
}
</style>| Prop | Type | Default | Description |
|---|---|---|---|
opacity | number | 0.08 | Strength, 0–1. 0.04–0.12 reads as texture; more reads as static. |
animate | boolean | true | Flicker the grain by stepping its position at 12 fps. |
blend | 'overlay' | 'soft-light' | 'multiply' | 'screen' | 'normal' | 'overlay' | Blend mode against the layers beneath. |
size | number | 200 | Tile size in px; smaller is finer grain. |
The grain holds still.
ClientRouterCSS-only; nothing to rebind.
Replaces: Noise (React Bits) · grainy gradient canvases. See the migration table.
Ambient gradient field: three soft colour fields drifting at three coprime speeds, a slow hue rotation so the colours themselves move, and a film-grain layer stepped at 12 fps.
A field of dots behind content, faded toward the edges, with an optional slow pulse.
A hairline grid behind content, faded toward the edges, optionally drifting one cell at a time.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown