# Film Grain and Flicker Over a Foggy Photo Hero

A shadcn/ui block from the [@weinc](https://we.inc/components?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/weinc/film-grain-and-flicker-over-a-foggy-photo-hero).

- Page, with a live preview: https://shadcn.exchange/r/weinc/film-grain-and-flicker-over-a-foggy-photo-hero
- Screenshot: https://shadcn.exchange/shot/weinc/film-grain-and-flicker-over-a-foggy-photo-hero/light.png
- Registry item: https://we.inc/r/film-grain-and-flicker-over-a-foggy-photo-hero.json
- Listed: 2026-10-28, source last changed 2026-10-28

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/weinc/film-grain-and-flicker-over-a-foggy-photo-hero.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Files

### `components/we-inc/film-grain-and-flicker-over-a-foggy-photo-hero.tsx`

```tsx
import { useEffect, useRef } from "react";

const PHOTO = "https://images.pexels.com/photos/29546724/pexels-photo-29546724.jpeg?auto=compress&cs=tinysrgb&w=1600";

const css = `
  .w4fgr { --w4fgr-grain: .34; --w4fgr-flicker: .07; --w4fgr-shade: 14, 22, 18;
    position: relative; isolation: isolate; overflow: hidden; display: flex; align-items: flex-end; min-height: 600px; background: #1a211d;
    color: #eef0e8; font-family: ui-sans-serif, system-ui, sans-serif; }
  .w4fgr-film { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
  /* the photo runs past the right edge on purpose: the source has a dark strip there */
  .w4fgr-photo { position: absolute; left: -12px; top: -12px; width: calc(100% + 80px); height: calc(100% + 24px); max-width: none; object-fit: cover; object-position: 50% 60%;
    animation: w4fgr-weave 2.4s steps(1) infinite; }
  .w4fgr-scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(var(--w4fgr-shade), .15) 0%, rgba(var(--w4fgr-shade), .35) 45%, rgba(var(--w4fgr-shade), .9) 100%),
    linear-gradient(90deg, rgba(var(--w4fgr-shade), .55) 0%, rgba(var(--w4fgr-shade), 0) 65%),
    radial-gradient(120% 90% at 50% 40%, transparent 50%, rgba(8, 10, 9, .6) 100%); }
  .w4fgr-flick { position: absolute; inset: 0; background: #f4f1e6; opacity: 0; animation: w4fgr-flick 1.3s steps(1) infinite; }
  .w4fgr-film::after { content: ""; position: absolute; inset: -120px; opacity: var(--w4fgr-grain); mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    animation: w4fgr-grain .6s steps(1) infinite; }
  .w4fgr[data-w4fgr-off] .w4fgr-film *, .w4fgr[data-w4fgr-off] .w4fgr-film::after { animation-play-state: paused; }
  @keyframes w4fgr-grain { 0% { transform: translate(0, 0); } 17% { transform: translate(-44px, 26px); } 33% { transform: translate(31px, -52px); }
    50% { transform: translate(-18px, -9px); } 67% { transform: translate(56px, 38px); } 83% { transform: translate(-61px, -33px); } }
  @keyframes w4fgr-weave { 0% { transform: translate(0, 0); } 25% { transform: translate(1px, -1px); } 50% { transform: translate(0, 1px); } 75% { transform: translate(-1px, 0); } }
  @keyframes w4fgr-flick { 0% { opacity: 0; } 20% { opacity: var(--w4fgr-flicker); } 30% { opacity: 0; } 62% { opacity: calc(var(--w4fgr-flicker) * .5); } 70% { opacity: 0; } }
  .w4fgr-inner { width: 100%; max-width: 72rem; margin: 0 auto; padding: 120px 24px 64px; box-sizing: border-box; }
  .w4fgr-kicker { margin: 0; font-family: ui-monospace, Menlo, monospace; font-size: 13px; letter-spacing: .14em; text-transform: uppercase; color: #cfd8c4; }
  .w4fgr-title { margin: 14px 0 0; max-width: 40rem; font-family: Georgia, "Times New Roman", serif; font-size: clamp(36px, 6vw, 64px); font-weight: 400; line-height: 1.04; letter-spacing: -.01em; }
  .w4fgr-copy { margin: 18px 0 0; max-width: 32rem; font-size: 17px; line-height: 1.6; color: #dfe4d8; }
  .w4fgr-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }
  .w4fgr-btn { display: inline-flex; align-items: center; min-height: 48px; padding: 0 24px; border-radius: 4px; font-size: 15px; font-weight: 600; text-decoration: none; }
  .w4fgr-btn.main { background: #eef0e8; color: #1a211d; }
  .w4fgr-btn.main:hover { background: #fff; }
  .w4fgr-btn.alt { color: #eef0e8; border: 1px solid rgba(238, 240, 232, .55); }
  .w4fgr-btn.alt:hover { background: rgba(238, 240, 232, .1); }
  .w4fgr-btn:focus-visible { outline: 3px solid #eef0e8; outline-offset: 3px; }
  @media (prefers-reduced-motion: reduce) { .w4fgr-film *, .w4fgr-film::after { animation: none !important; } }
`;

export default function FilmGrainAndFlickerOverAFoggyPhotoHero() {
  const rootRef = useRef<HTMLElement>(null);

  // Pauses the grain, weave and flicker while the hero is off screen.
  useEffect(() => {
    const root = rootRef.current;
    if (!root) return;
    const io = new IntersectionObserver(([en]) => root.toggleAttribute("data-w4fgr-off", !en.isIntersecting));
    io.observe(root);
    return () => io.disconnect();
  }, []);

  return (
    <section ref={rootRef} className="w4fgr">
      <style>{css}</style>
      <div className="w4fgr-film" aria-hidden="true">
        <img className="w4fgr-photo" src={PHOTO} alt="A small wooden hut under tall pines in fog" width={1600} height={1067} />
        <div className="w4fgr-scrim" />
        <div className="w4fgr-flick" />
      </div>
      <div className="w4fgr-inner">
        <p className="w4fgr-kicker">Hollow Pine Huts</p>
        <h2 className="w4fgr-title">One room, one stove, and the pines all around.</h2>
        <p className="w4fgr-copy">Four timber huts in a spruce wood at the end of a gravel track. Bring boots; the nearest shop is a forty minute walk.</p>
        <div className="w4fgr-row">
          <a href="#" className="w4fgr-btn main">See the huts</a>
          <a href="#" className="w4fgr-btn alt">How to get here</a>
        </div>
      </div>
    </section>
  );
}
```
