Grain
betaFilm-grain overlay from a cached SVG-noise tile, with blend modes and an optional compositor-only flicker — texture for gradients and heroes.
Grain over a gradient
minimal
Blend modes, opacity, scale, flicker
Compare every blend mode on a light and a dark surface.
full
On a mesh gradient
The classic pairing: MeshGradient with Grain as its last child.
composition
A little grain turns a gradient into a print.
Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/grain.jsonCopies the source into components/kura/grain with its dependencies (kura-theme, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- Adding a print-like finish on top of a mesh-gradient, aurora or photo.
- Hiding colour banding on large, low-contrast gradients.
- Giving a flat hero or empty-state panel a subtle tactile texture.
Reach for something else when
- As the background itself — combine it with mesh-gradient or aurora.
- Over body text or dense UI: noise reduces legibility.
- Signalling activity — grain is texture; use border-beam or thinking-indicator.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| opacity | number | 0.12 | Strength of the grain, 0–1. 0.08–0.2 is subtle; 0.3+ is stylised. |
| blend | "overlay" | "soft-light" | "multiply" | "screen" | "normal" | "overlay" | mix-blend-mode. overlay works on mid and dark tones; multiply suits light surfaces; screen suits very dark ones. |
| scale | number | 1 | Grain size multiplier: 1 is fine film grain, 2–3 is coarse. |
| animated | boolean | false | Flicker the grain like projected film (a stepped transform, compositor-only). Off by default. |
| fps | number | 12 | Flicker rate when animated. 8–15 reads as film; higher looks like TV static. |
All native attributes of the root element, className and ref pass through.
Anatomy
- GrainAbsolutely positioned overlay (`inset-0`, pointer-events none, overflow hidden) carrying opacity and blend mode. `data-animated` mirrors `animated`.
data-slot="grain" - LayerOversized noise layer (200%) that the flicker translates.
data-slot="grain-layer"
Accessibility
- Decorative and aria-hidden; it never receives pointer events.
- Keep opacity low over text; grain can lower perceived contrast.
- Reduced motion: The flicker stops; the grain stays as a static texture.
Notes for agents
- The parent must be positioned (`relative`) — Grain is `absolute inset-0`. Render it last so it sits on top, and lift text above it with `relative z-10` if needed.
- Server-safe: no hooks, no 'use client'.
- On near-white surfaces `overlay` is almost invisible by design; use `blend="multiply"` with opacity ~0.06 there.
- One Grain per surface; it tiles a 180px image, so cost does not grow with size.