25
Componentregistry:component

Grain & Paper Overlay

A lightweight, pointer-transparent film grain, paper or fibre texture you can drop on any section or fix over the whole page. SVG noise, optional tint and flicker, server-component safe.

Installation

$ npx shadcn@latest add @creative-ui/grain-overlay

On an older shadcn CLI, use the URL: npx shadcn@latest add https://ui.krutikraut.com/r/grain-overlay.json

Preview

Section · 04

Printed on paper, not pixels.

The overlay sits above this content, ignores the pointer and reads on light and dark alike.

Get startedRead the notes
Variant
Surface
Opacity0.25

Usage

Example.tsx
import { GrainOverlay } from "@/components/grain-overlay"

export function Section() {
  return (
    <section className="relative bg-[#f4efe6] p-16">
      <h2>Made by hand</h2>
      <GrainOverlay variant="paper" />
    </section>
  )
}

// Or once, over the whole page:
<GrainOverlay fixed animate opacity={0.12} />

Props

PropTypeDefaultDescription
variant"grain" | "paper" | "fiber""grain"grain is fine film grain. paper is warm stock with fine tooth and faint mottling. fiber is handmade paper with loose strands. Sets the defaults for the props below.
opacitynumberper variantLayer opacity, 0 to 1. Grain 0.25, paper 0.45, fiber 0.5.
frequencynumber | [number, number]per variantFrequency of the variant's main noise. Higher is finer. A pair sets x and y separately. Grain 0.85, paper tooth 0.9, fiber strands 0.03.
octavesnumberper variantOctaves of the main noise. More add finer detail. Grain 4, paper 3, fiber 1.
contrastnumberper variantHow hard the specks are cut from the noise. Higher is crisper and stronger. Grain 2.2, paper 1.8, fiber 28.
seednumber2Noise seed, so two overlays can differ or match.
sizenumberper variantTile size in px. Grain 240, paper and fiber 480.
colorstring—Paint every speck in one colour. Any CSS colour works, including var(--primary) and currentColor. When omitted, the texture uses dark and light specks.
blendCSSProperties["mixBlendMode"]"normal"CSS mix-blend-mode. The texture is already dark and light specks on transparency, so it needs no blend mode. Set one for a specific look, such as soft-light over photos.
fixedbooleanfalseCover the viewport instead of the nearest positioned ancestor.
animatebooleanfalseFilm flicker: the grain jumps between eight held frames at about 10fps. It runs on the compositor and stays still for users who prefer reduced motion.

Also accepts every div prop, which is spread onto the root element.

Also exported

noiseTexture({ variant?, frequency?, octaves?, contrast?, seed?, size? })
Builds a variant's tiling SVG noise as a CSS url(). The alpha channel carries the texture, so it works as a background-image or a mask-image.
grainPresets
The default size, opacity, colours and noise layers for each variant.

Notes

  • Place it inside an element with position: relative. It fills that element and never blocks clicks.
  • It reads on light, dark and photographic surfaces without a blend mode, because each pixel is either transparent or a dark or light speck.
  • fixed uses z-50. Override it with className if it covers dialogs or toasts.
  • It is hidden when printing and in forced-colours mode.
  • No client JavaScript, so it is safe in React Server Components. With animate, the keyframes are added to the page once, however many overlays use them.

Dependencies

shadcn/utils