Updated 2026-10-14. 17 effects.
To animate a background in CSS, layer gradients or repeating patterns on a pseudo-element and move them with @keyframes that change only transform or opacity. Every effect below works that way, copies as plain HTML and CSS or as React, stops for reduced motion and pauses when scrolled away.
Pick by the mood of the section above it. Fine textures and slow light suit editorial pages and portfolios, where the background should be felt more than seen. Grids, beams and rings give product pages a technical feel without a single image file.
None of these need a library. The CSS version is the whole effect; its few lines of script only watch whether the block is on screen. Paste the snippet at the top of a section, put your content inside it, and change the colour variables at the top of its style block.
| Effect | Built with | Size (gzipped) | Running cost | Browser support |
|---|---|---|---|---|
| Slatted Blind Sunlight on a Plaster Wall | CSS only | 2.1 KB | Three layers move by transform or opacity on the compositor; the only script is an observer that pauses it off screen. | - |
| Breathing Halftone Dot Field | CSS only | 1.9 KB | The contrast filter repaints the dot layer every frame, so it costs more than a transform; keep it to one block per page. | - |
| Brushed Steel with a Sliding Sheen | CSS only | 1.9 KB | Two skewed bands move by transform with blend modes; the brushed noise is painted once. | - |
| Cinema Projector Beam with Drifting Dust | CSS only | 2.3 KB | Opacity flicker and two transformed dust tiles run on the compositor; the blurs are drawn once. | - |
| Dot Matrix with Travelling Light | CSS only | 1.9 KB | Two bands slide with transform under a fixed mask; the dot pattern itself never repaints. | - |
| Drifting Contour Lines at Dawn | CSS only | 2.2 KB | The SVG filter renders once; afterwards three layers move with transform only, though the first paint costs a few milliseconds. | - |
| Film Grain and Flicker Over a Foggy Photo Hero | CSS only | 2.1 KB | Three small keyframe loops move or fade layers that are already painted, and an observer pauses them while the hero is out of view. | All current browsers |
| Ink Editorial Section With Grain and Breathing Vignette | CSS only | 2.2 KB | The grain layer jumps between eight fixed offsets and two soft gradients fade and drift slowly, all on transform and opacity, paused while off screen. | All current browsers |
| Isometric Cube Field with Light Ripple | CSS only | 2.5 KB | Three scaling rings and a pulse inside one masked layer; masks are static, rings use transform and opacity only. | - |
| Layered Diagonal Stripe Bands | CSS only | 1.8 KB | Three tiled layers move with translateX on the compositor; nothing repaints while they slide. | - |
| Lighthouse Beam Through Sea Fog | CSS only | 2.4 KB | One large rotating layer plus three drifting fog layers, all transform or opacity; no script loop. | - |
| Linen Weave Under a Moving Lamp | CSS only | 1.9 KB | One glow layer moves by transform with an overlay blend; the weave and noise are painted once. | - |
| Perspective Floor Grid with Horizon Glow | CSS only | 2.2 KB | Two transformed layers and one opacity fade on the compositor; no JavaScript beyond an off-screen pause. | - |
| Reeded Glass with Passing Night Lights | CSS only | 1.8 KB | Six blurred gradient shapes move by transform under a static mask; no filters are animated. | - |
| Scanner Light Bar Under Copier Glass | CSS only | 2.1 KB | A single full-height layer moves with transform and blends with screen; everything else is static paint. | - |
| Sonar Pulse Rings | CSS only | 2 KB | Three scaling rings and three blinking dots, all transform and opacity; the chart is painted once. | - |
| Terracotta Poster Block With Coarse Canvas Grain | Canvas 2D | 2.3 KB | The canvas draws one small tile a single time on load; after that only a CSS loop jumps that tile between five offsets, paused off screen. | All current browsers; SVG grain if canvas is blocked |
Late afternoon sun falls through a window blind and lands on a warm plaster wall as a stack of soft golden bars. Over half a minute the patch drifts sideways and changes angle, the way real sunlight creeps across a room, while the slats themselves tilt very slightly. It suits hand-made goods, cafes, studios and any brand that wants its homepage to feel lived in. On phones the light still crosses behind the heading, which keeps its dark ink colour for contrast.
<section class="vbl">
<div class="vbl-fx" aria-hidden="true"><div class="vbl-patch"><div class="vbl-slats"></div><div class="vbl-slats vbl-pane2"></div></div><div class="vbl-bounce"></div><div class="vbl-grain"></div><div class="vbl-shade"></div></div>
<div class="vbl-hero"><p class="vbl-eyebrow">Alder Clay Studio</p><h1 class="vbl-title">Stoneware thrown by hand, fired in small batches</h1><p class="vbl-copy">Mugs, bowls and serving plates made in a sunny back room on Fern Street. New kiln loads go on the shelves every second Saturday.</p><a class="vbl-btn" href="#">Visit the studio</a></div>
</section>
<style>
/* Change these: wall colour, sunlight colour, how long one slow pass takes, slat spacing, text colour */
.vbl{--vbl-wall:#ddc9ad;--vbl-sun:rgba(255,214,150,.9);--vbl-speed:30s;--vbl-slat:36px;--vbl-ink:#3a2718}
.vbl{position:relative;isolation:isolate;overflow:hidden;min-height:560px;display:flex;align-items:center;background:linear-gradient(180deg,var(--vbl-wall),#cdb593);color:var(--vbl-ink);font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.vbl-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.vbl-fx *{position:absolute;pointer-events:none}
.vbl-hero{position:relative;z-index:1;width:100%;max-width:1120px;margin:0 auto;padding:72px 24px;box-sizing:border-box}
.vbl-eyebrow{margin:0 0 14px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.vbl-title{margin:0;max-width:15ch;font-size:clamp(34px,5.6vw,60px);line-height:1.04;letter-spacing:-.025em;font-weight:650}
.vbl-copy{margin:20px 0 30px;max-width:44ch;font-size:clamp(16px,1.6vw,18px);line-height:1.6}
.vbl-btn{display:inline-block;padding:13px 24px;border-radius:999px;font-size:15px;font-weight:600;text-decoration:none;transition:transform .2s ease}
.vbl-btn:hover{transform:translateY(-1px)}
.vbl-btn:focus-visible{outline:3px solid currentColor;outline-offset:4px}
.vbl[data-paused] *,.vbl[data-paused] *::before,.vbl[data-paused] *::after{animation-play-state:paused!important}
.vbl[data-reduced] *,.vbl[data-reduced] *::before,.vbl[data-reduced] *::after{animation:none!important}
@media (prefers-reduced-motion:reduce){.vbl *,.vbl *::before,.vbl *::after{animation:none!important}}
.vbl-patch{width:62%;height:118%;left:38%;top:-6%;transform:rotate(-14deg) skewX(-20deg);mix-blend-mode:screen;filter:blur(2px);-webkit-mask-image:radial-gradient(75% 70% at 50% 45%,#000 55%,transparent 100%);mask-image:radial-gradient(75% 70% at 50% 45%,#000 55%,transparent 100%);animation:vbl-slide var(--vbl-speed) ease-in-out infinite alternate;animation-delay:calc(var(--vbl-speed) * -.45)}
.vbl-slats{top:0;bottom:0;left:0;width:47.5%;background:repeating-linear-gradient(180deg,var(--vbl-sun) 0,var(--vbl-sun) calc(var(--vbl-slat) * .58),rgba(255,206,140,0) calc(var(--vbl-slat) * .74),rgba(255,206,140,0) var(--vbl-slat));animation:vbl-tilt 11s ease-in-out infinite alternate}
.vbl-pane2{left:51.5%;width:48.5%}
.vbl-bounce{width:70%;height:90%;right:-10%;bottom:-30%;background:radial-gradient(closest-side,rgba(255,190,120,.35),rgba(255,190,120,0));animation:vbl-glow 13s ease-in-out infinite alternate}
.vbl-grain{inset:0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .42 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");mix-blend-mode:multiply;opacity:.7}
.vbl-shade{inset:0;background:radial-gradient(130% 100% at 70% 30%,rgba(80,50,20,0) 45%,rgba(80,50,20,.22))}
.vbl-eyebrow{color:#7a431c}
.vbl-copy{color:#4a3424}
.vbl-btn{background:var(--vbl-ink);color:#f7eddf}
@keyframes vbl-slide{from{transform:translate(-12%,-2%) rotate(-15deg) skewX(-22deg)}to{transform:translate(12%,4%) rotate(-11deg) skewX(-15deg)}}
@keyframes vbl-tilt{from{transform:scaleY(.95) translateY(-4px)}to{transform:scaleY(1.04) translateY(5px)}}
@keyframes vbl-glow{from{opacity:.55;transform:translateX(-4%)}to{opacity:1;transform:translateX(4%)}}
</style>
<script>
(function () {
// Pauses the animation while the block is off screen, and stops it for reduced motion.
var roots = document.querySelectorAll(".vbl");
var still = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
roots.forEach(function (root) {
if (still) { root.setAttribute("data-reduced", ""); return; }
if (!("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) root.removeAttribute("data-paused");
else root.setAttribute("data-paused", "");
}).observe(root);
});
})();
</script>
With reduced motion: The sunlight bars rest in one tilted patch across the right of the wall, with grain and shading unchanged.
Running cost: Three layers move by transform or opacity on the compositor; the only script is an observer that pauses it off screen.
Full page for Slatted Blind Sunlight on a Plaster Wall
A field of halftone dots on cream paper swells and shrinks as two soft shadows wander beneath it, so the printed tone seems to breathe. A contrast filter turns the blurred shapes into crisp dots, then two blend layers recolour them in navy ink. It suits print studios, zines, editorial sites and anything with a handmade press feel.
<section class="htn">
<div class="htn-fx" aria-hidden="true"><div class="htn-screen"><div class="htn-tone"></div><div class="htn-blob htn-a"></div><div class="htn-blob htn-b"></div><div class="htn-dots"></div></div><div class="htn-ink"></div><div class="htn-paper"></div><div class="htn-grain"></div></div>
<div class="htn-hero"><p class="htn-eyebrow">Fieldnote Press</p><h1 class="htn-title">Riso prints, zines and posters in two inks</h1><p class="htn-copy">Bring a file or a sketch. We print short runs on recycled stock and you can watch the drum turn while you wait.</p><a class="htn-btn" href="#">See this month's paper</a></div>
</section>
<style>
/* Change these: paper colour, ink colour, dot cell size, seconds for one slow swell */
.htn{--htn-paper:#f2e8d5;--htn-ink:#1d3452;--htn-cell:11px;--htn-speed:14s}
.htn{position:relative;isolation:isolate;overflow:hidden;min-height:560px;display:flex;align-items:center;background:var(--htn-paper);color:var(--htn-ink);font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.htn-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.htn-fx *{position:absolute;pointer-events:none}
.htn-hero{position:relative;z-index:1;width:100%;max-width:1120px;margin:0 auto;padding:72px 24px;box-sizing:border-box}
.htn-eyebrow{margin:0 0 14px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.htn-title{margin:0;max-width:15ch;font-size:clamp(34px,5.6vw,60px);line-height:1.04;letter-spacing:-.025em;font-weight:650}
.htn-copy{margin:20px 0 30px;max-width:44ch;font-size:clamp(16px,1.6vw,18px);line-height:1.6}
.htn-btn{display:inline-block;padding:13px 24px;border-radius:999px;font-size:15px;font-weight:600;text-decoration:none;transition:transform .2s ease}
.htn-btn:hover{transform:translateY(-1px)}
.htn-btn:focus-visible{outline:3px solid currentColor;outline-offset:4px}
.htn[data-paused] *,.htn[data-paused] *::before,.htn[data-paused] *::after{animation-play-state:paused!important}
.htn[data-reduced] *,.htn[data-reduced] *::before,.htn[data-reduced] *::after{animation:none!important}
@media (prefers-reduced-motion:reduce){.htn *,.htn *::before,.htn *::after{animation:none!important}}
.htn-screen{inset:0;background:#fff;filter:contrast(18)}
.htn-tone{inset:0;background:linear-gradient(100deg,#fff 50%,#e8e8e8 72%,#cdcdcd 100%)}
.htn-blob{aspect-ratio:1;border-radius:50%;background:radial-gradient(closest-side,#8c8c8c,#bcbcbc 45%,#ececec 78%,rgba(255,255,255,0));mix-blend-mode:multiply}
.htn-a{width:70%;right:-26%;top:-36%;animation:htn-a var(--htn-speed) ease-in-out infinite alternate;animation-delay:calc(var(--htn-speed) * -.3)}
.htn-b{width:58%;right:2%;bottom:-48%;animation:htn-b calc(var(--htn-speed) * 1.35) ease-in-out infinite alternate;animation-delay:calc(var(--htn-speed) * -.7)}
.htn-dots{inset:0;background:radial-gradient(closest-side,#6e6e6e,#fff) 0 0/var(--htn-cell) var(--htn-cell);mix-blend-mode:multiply}
.htn-ink{inset:0;background:var(--htn-ink);mix-blend-mode:screen}
.htn-paper{inset:0;background:var(--htn-paper);mix-blend-mode:multiply}
.htn-grain{inset:0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.75' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .35 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");mix-blend-mode:multiply;opacity:.6}
.htn-eyebrow{color:#8a3b22}
.htn-copy{color:#2c3e55}
.htn-btn{background:var(--htn-ink);color:var(--htn-paper)}
@keyframes htn-a{from{transform:translate(0,0) scale(.9)}to{transform:translate(-8%,16%) scale(1.1)}}
@keyframes htn-b{from{transform:translate(0,0) scale(1.1)}to{transform:translate(-18%,-12%) scale(.85)}}
@media (max-width:700px){.htn-tone{background:linear-gradient(180deg,#fff 55%,#d4d4d4)}.htn-a{width:110%;right:-40%;top:auto;bottom:-55%}.htn-b{width:80%;right:auto;left:-40%;bottom:-50%}}
</style>
<script>
(function () {
// Pauses the animation while the block is off screen, and stops it for reduced motion.
var roots = document.querySelectorAll(".htn");
var still = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
roots.forEach(function (root) {
if (still) { root.setAttribute("data-reduced", ""); return; }
if (!("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) root.removeAttribute("data-paused");
else root.setAttribute("data-paused", "");
}).observe(root);
});
})();
</script>
With reduced motion: The two ink shadows hold still, leaving one fixed patch of graded dots in the corner.
Running cost: The contrast filter repaints the dot layer every frame, so it costs more than a transform; keep it to one block per page.
A dark graphite steel plate with fine horizontal brush marks, crossed every few seconds by a slanted sheen that lifts the grain as it travels, followed by a wider, softer reflection. The brushing is generated noise stretched along one axis. It fits machine shops, hardware, tools, watches and any industrial brand.
<section class="bst">
<div class="bst-fx" aria-hidden="true"><div class="bst-brush"></div><div class="bst-sheen"></div><div class="bst-sheen bst-wide"></div><div class="bst-edge"></div></div>
<div class="bst-hero"><p class="bst-eyebrow">Kessler Tool Works</p><h1 class="bst-title">Precision parts machined from solid steel</h1><p class="bst-copy">Five-axis milling and turning for small runs and prototypes, with inspection reports shipped alongside every order.</p><a class="bst-btn" href="#">Request a quote</a></div>
</section>
<style>
/* Change these: metal colour, sheen colour, seconds between sheens, brush texture strength */
.bst{--bst-metal:#2b3035;--bst-sheen:rgba(230,236,244,.13);--bst-speed:11s;--bst-grain:.85}
.bst{position:relative;isolation:isolate;overflow:hidden;min-height:560px;display:flex;align-items:center;background:linear-gradient(180deg,#323840,var(--bst-metal) 55%,#24282d);color:#f2f4f6;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.bst-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.bst-fx *{position:absolute;pointer-events:none}
.bst-hero{position:relative;z-index:1;width:100%;max-width:1120px;margin:0 auto;padding:72px 24px;box-sizing:border-box}
.bst-eyebrow{margin:0 0 14px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.bst-title{margin:0;max-width:15ch;font-size:clamp(34px,5.6vw,60px);line-height:1.04;letter-spacing:-.025em;font-weight:650}
.bst-copy{margin:20px 0 30px;max-width:44ch;font-size:clamp(16px,1.6vw,18px);line-height:1.6}
.bst-btn{display:inline-block;padding:13px 24px;border-radius:999px;font-size:15px;font-weight:600;text-decoration:none;transition:transform .2s ease}
.bst-btn:hover{transform:translateY(-1px)}
.bst-btn:focus-visible{outline:3px solid currentColor;outline-offset:4px}
.bst[data-paused] *,.bst[data-paused] *::before,.bst[data-paused] *::after{animation-play-state:paused!important}
.bst[data-reduced] *,.bst[data-reduced] *::before,.bst[data-reduced] *::after{animation:none!important}
@media (prefers-reduced-motion:reduce){.bst *,.bst *::before,.bst *::after{animation:none!important}}
.bst-brush{inset:0;opacity:var(--bst-grain);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.0015 .55' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .22 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.003 .9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .45 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");mix-blend-mode:normal}
.bst-sheen{top:-25%;bottom:-25%;left:-50%;width:42%;background:linear-gradient(90deg,rgba(230,236,244,0),var(--bst-sheen) 42%,rgba(255,255,255,.2) 50%,var(--bst-sheen) 58%,rgba(230,236,244,0));mix-blend-mode:screen;transform:translateX(250%) skewX(-22deg);animation:bst-sweep var(--bst-speed) cubic-bezier(.55,0,.35,1) infinite;animation-delay:calc(var(--bst-speed) * -.2)}
.bst-wide{width:80%;left:-85%;opacity:.55;mix-blend-mode:soft-light;transform:translateX(120%) skewX(-22deg);animation-duration:calc(var(--bst-speed) * 1.9);animation-delay:calc(var(--bst-speed) * -1.2)}
.bst-edge{inset:0;background:linear-gradient(180deg,rgba(255,255,255,.14) 0,rgba(255,255,255,0) 3px),linear-gradient(0deg,rgba(0,0,0,.35) 0,rgba(0,0,0,0) 4px),linear-gradient(90deg,rgba(20,23,27,.55) 0,rgba(20,23,27,0) 55%)}
.bst-eyebrow{color:#b3c0cd}
.bst-copy{color:#ccd3da}
.bst-btn{background:#e8edf2;color:#1b1f24}
@keyframes bst-sweep{0%{transform:translateX(0) skewX(-22deg)}62%,100%{transform:translateX(420%) skewX(-22deg)}}
</style>
<script>
(function () {
// Pauses the animation while the block is off screen, and stops it for reduced motion.
var roots = document.querySelectorAll(".bst");
var still = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
roots.forEach(function (root) {
if (still) { root.setAttribute("data-reduced", ""); return; }
if (!("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) root.removeAttribute("data-paused");
else root.setAttribute("data-paused", "");
}).observe(root);
});
})();
</script>
With reduced motion: One sheen rests across the middle of the plate, so the brushing still shows its direction.
Running cost: Two skewed bands move by transform with blend modes; the brushed noise is painted once.
From a small port high in the corner, a projector beam cuts across a dark room, flickering faintly the way film light does, while specks of dust drift slowly through it and vanish at its edges. The beam is two clipped gradients with blur, the dust is tiled dots. It suits cinemas, film festivals, theatres, photographers and video studios.
<section class="pjb">
<div class="pjb-fx" aria-hidden="true"><div class="pjb-halo"></div><div class="pjb-beam"></div><div class="pjb-cone"><div class="pjb-dust pjb-d1"></div><div class="pjb-dust pjb-d2"></div></div><div class="pjb-port"></div><div class="pjb-grain"></div></div>
<div class="pjb-hero"><p class="pjb-eyebrow">The Rialto Picture House</p><h1 class="pjb-title">One screen, ninety seats and a real reel of film</h1><p class="pjb-copy">Classic and new releases shown from a projector that has run since the cinema reopened. Tea, cake and the occasional intermission.</p><a class="pjb-btn" href="#">See what is showing</a></div>
</section>
<style>
/* Change these: room colour, beam colour (r,g,b), beam strength, seconds for one dust drift */
.pjb{--pjb-room:#13100e;--pjb-beam:255,234,196;--pjb-strength:.34;--pjb-drift:46s}
.pjb{position:relative;isolation:isolate;overflow:hidden;min-height:560px;display:flex;align-items:center;background:radial-gradient(70% 60% at 92% 0%,#2b221b 0,rgba(43,34,27,0) 65%),var(--pjb-room);color:#f3eadc;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.pjb-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.pjb-fx *{position:absolute;pointer-events:none}
.pjb-hero{position:relative;z-index:1;width:100%;max-width:1120px;margin:0 auto;padding:72px 24px;box-sizing:border-box}
.pjb-eyebrow{margin:0 0 14px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.pjb-title{margin:0;max-width:15ch;font-size:clamp(34px,5.6vw,60px);line-height:1.04;letter-spacing:-.025em;font-weight:650}
.pjb-copy{margin:20px 0 30px;max-width:44ch;font-size:clamp(16px,1.6vw,18px);line-height:1.6}
.pjb-btn{display:inline-block;padding:13px 24px;border-radius:999px;font-size:15px;font-weight:600;text-decoration:none;transition:transform .2s ease}
.pjb-btn:hover{transform:translateY(-1px)}
.pjb-btn:focus-visible{outline:3px solid currentColor;outline-offset:4px}
.pjb[data-paused] *,.pjb[data-paused] *::before,.pjb[data-paused] *::after{animation-play-state:paused!important}
.pjb[data-reduced] *,.pjb[data-reduced] *::before,.pjb[data-reduced] *::after{animation:none!important}
@media (prefers-reduced-motion:reduce){.pjb *,.pjb *::before,.pjb *::after{animation:none!important}}
.pjb-beam,.pjb-halo{inset:0;animation:pjb-flicker 2.6s steps(1) infinite}
.pjb-beam{filter:blur(6px)}
.pjb-halo{filter:blur(28px);opacity:.7;animation-delay:-1.1s}
.pjb-beam::before,.pjb-halo::before{content:"";position:absolute;inset:0;background:radial-gradient(140% 140% at 93% 6%,rgba(var(--pjb-beam),var(--pjb-strength)) 0,rgba(var(--pjb-beam),calc(var(--pjb-strength) * .4)) 40%,rgba(var(--pjb-beam),0) 92%)}
.pjb-beam::before{clip-path:polygon(92% 4%,94.5% 8%,46% 100%,0 100%,0 86%)}
.pjb-halo::before{clip-path:polygon(91% 2%,96% 10%,58% 100%,0 100%,0 70%)}
.pjb-cone{inset:0;overflow:hidden;clip-path:polygon(92% 4%,94.5% 8%,46% 100%,0 100%,0 86%);-webkit-mask-image:radial-gradient(130% 130% at 93% 6%,#000 0,rgba(0,0,0,.35) 75%);mask-image:radial-gradient(130% 130% at 93% 6%,#000 0,rgba(0,0,0,.35) 75%)}
.pjb-dust{inset:-300px;background-repeat:repeat}
.pjb-d1{background-image:radial-gradient(1.8px 1.8px at 20px 30px,rgba(255,242,220,.95),rgba(255,242,220,0)),radial-gradient(1px 1px at 120px 80px,rgba(255,242,220,.8),rgba(255,242,220,0)),radial-gradient(2.2px 2.2px at 170px 190px,rgba(255,242,220,.9),rgba(255,242,220,0)),radial-gradient(1px 1px at 60px 150px,rgba(255,242,220,.7),rgba(255,242,220,0)),radial-gradient(1.2px 1.2px at 200px 20px,rgba(255,242,220,.85),rgba(255,242,220,0));background-size:220px 240px;animation:pjb-d1 var(--pjb-drift) linear infinite}
.pjb-d2{filter:blur(1px);background-image:radial-gradient(2.4px 2.4px at 40px 60px,rgba(255,236,205,.7),rgba(255,236,205,0)),radial-gradient(2px 2px at 250px 210px,rgba(255,236,205,.6),rgba(255,236,205,0)),radial-gradient(3px 3px at 150px 280px,rgba(255,236,205,.5),rgba(255,236,205,0));background-size:330px 320px;opacity:.9;animation:pjb-d2 calc(var(--pjb-drift) * 1.5) linear infinite;animation-delay:calc(var(--pjb-drift) * -.5)}
.pjb-port{right:5.5%;top:4.5%;width:26px;height:16px;border-radius:50%;background:radial-gradient(closest-side,#fffaf0,rgba(255,226,170,.7) 50%,rgba(255,226,170,0));box-shadow:0 0 44px 20px rgba(255,220,160,.22)}
.pjb-grain{inset:0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .5 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");opacity:.05;mix-blend-mode:screen}
.pjb-eyebrow{color:#e6b878}
.pjb-copy{color:#d8cdbd}
.pjb-btn{background:#f3eadc;color:#13100e}
@keyframes pjb-flicker{0%{opacity:1}17%{opacity:.92}23%{opacity:1}51%{opacity:.95}58%{opacity:.88}61%{opacity:1}84%{opacity:.96}}
@keyframes pjb-d1{from{transform:translate(0,0)}to{transform:translate(-220px,240px)}}
@keyframes pjb-d2{from{transform:translate(0,0)}to{transform:translate(330px,-320px)}}
</style>
<script>
(function () {
// Pauses the animation while the block is off screen, and stops it for reduced motion.
var roots = document.querySelectorAll(".pjb");
var still = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
roots.forEach(function (root) {
if (still) { root.setAttribute("data-reduced", ""); return; }
if (!("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) root.removeAttribute("data-paused");
else root.setAttribute("data-paused", "");
}).observe(root);
});
})();
</script>
With reduced motion: The beam holds steady with no flicker, and the dust stays frozen as still specks in the light.
Running cost: Opacity flicker and two transformed dust tiles run on the compositor; the blurs are drawn once.
A pale grey dot grid on near-white, where a slate-blue band of light sweeps across and colours only the dots it passes, followed later by a smaller copper band going the other way. A CSS mask shaped like the grid limits the colour to the dots. It suits finance, consulting and software sites with a light, tidy look.
<section class="dmx-root">
<div class="dmx-art" aria-hidden="true">
<div class="dmx-dots"></div>
<div class="dmx-lit"><div class="dmx-band"></div><div class="dmx-echo"></div></div>
<div class="dmx-calm"></div>
<div class="dmx-edge"></div>
</div>
<div class="dmx-hero">
<p class="dmx-eyebrow">Bookkeeping for architects</p>
<h1 class="dmx-title">Every invoice, filed before the coffee cools.</h1>
<p class="dmx-copy">Receipts, fee stages and VAT returns kept in order by two accountants who read drawings as well as ledgers.</p>
<a class="dmx-btn" href="#">Book a 20 minute call</a>
</div>
</section>
<style>
.dmx-root{
/* Change these: */
--dmx-gap: 20px; /* distance between dots */
--dmx-dot: 1.8px; /* dot radius */
--dmx-lit: 41,82,150; /* colour of the travelling light as r,g,b */
--dmx-speed: 9s; /* time for the band to cross */
}
.dmx-root{position:relative;display:flex;align-items:center;min-height:600px;isolation:isolate;overflow:hidden;background:#f6f7f9;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.dmx-art{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.dmx-dots{position:absolute;inset:0;background-image:radial-gradient(circle,#c3cad5 var(--dmx-dot),transparent calc(var(--dmx-dot) + .6px));background-size:var(--dmx-gap) var(--dmx-gap);background-position:center}
.dmx-lit{position:absolute;inset:0;
-webkit-mask-image:radial-gradient(circle,#000 calc(var(--dmx-dot) + .7px),transparent calc(var(--dmx-dot) + 1.3px));mask-image:radial-gradient(circle,#000 calc(var(--dmx-dot) + .7px),transparent calc(var(--dmx-dot) + 1.3px));
-webkit-mask-size:var(--dmx-gap) var(--dmx-gap);mask-size:var(--dmx-gap) var(--dmx-gap);-webkit-mask-position:center;mask-position:center}
.dmx-band{position:absolute;top:-40%;bottom:-40%;left:0;width:45%;will-change:transform;transform:translateX(-80%) rotate(14deg);
background:linear-gradient(90deg,transparent,rgba(var(--dmx-lit),.5) 25%,rgb(var(--dmx-lit)) 50%,rgba(var(--dmx-lit),.5) 75%,transparent);animation:dmx-pass var(--dmx-speed) cubic-bezier(.55,.1,.45,.9) infinite}
.dmx-echo{position:absolute;top:-40%;bottom:-40%;left:0;width:26%;will-change:transform;transform:translateX(330%) rotate(-10deg);
background:linear-gradient(90deg,transparent,rgba(196,110,60,.9) 50%,transparent);animation:dmx-back calc(var(--dmx-speed) * 1.5) cubic-bezier(.55,.1,.45,.9) infinite}
.dmx-calm{position:absolute;left:50%;top:50%;width:min(860px,120%);height:380px;transform:translate(-50%,-50%);border-radius:50%;background:radial-gradient(closest-side,rgba(246,247,249,.97) 35%,rgba(246,247,249,.6) 65%,transparent 100%)}
.dmx-edge{position:absolute;inset:0;background:linear-gradient(180deg,#f6f7f9,transparent 14%,transparent 86%,#f6f7f9)}
@keyframes dmx-pass{0%{transform:translateX(-80%) rotate(14deg)}100%{transform:translateX(260%) rotate(14deg)}}
@keyframes dmx-back{0%{transform:translateX(360%) rotate(-10deg)}100%{transform:translateX(-120%) rotate(-10deg)}}
.dmx-hero{position:relative;z-index:1;box-sizing:border-box;width:100%;max-width:720px;margin:0 auto;padding:112px 20px;text-align:center}
.dmx-hero > *{max-width:none}
.dmx-eyebrow{margin:0 0 18px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#2c4f8a}
.dmx-title{margin:0;font-size:clamp(2.1rem,5.4vw,3.6rem);line-height:1.06;letter-spacing:-.025em;font-weight:650;color:#121a2b;text-wrap:balance}
.dmx-copy{margin:20px auto 0;max-width:34em;font-size:clamp(1rem,1.6vw,1.125rem);line-height:1.6;color:#4a5568}
.dmx-btn{display:inline-flex;align-items:center;justify-content:center;min-height:46px;margin-top:32px;padding:0 24px;border-radius:8px;background:#1d3a6b;color:#ffffff;font-size:15px;font-weight:600;text-decoration:none;box-shadow:none;transition:transform .2s ease,background-color .2s ease}
.dmx-btn:hover{transform:translateY(-1px);background:#264a85}
.dmx-btn:focus-visible{outline:2px solid #2c4f8a;outline-offset:3px}
@media (max-width:640px){.dmx-hero{padding:88px 20px}}
.dmx-root[data-paused] *,.dmx-root[data-paused] *::before,.dmx-root[data-paused] *::after{animation-play-state:paused!important}
@media (prefers-reduced-motion: reduce){
.dmx-band{transform:translateX(10%) rotate(14deg)}.dmx-echo{transform:translateX(240%) rotate(-10deg)}
.dmx-root *,.dmx-root *::before,.dmx-root *::after{animation:none!important;transition:none!important}
}
</style>
<script>
(function () {
// Pauses the animation while the section is off screen. Not needed for reduced motion.
if (!("IntersectionObserver" in window)) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
var roots = document.querySelectorAll(".dmx-root");
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) entry.target.removeAttribute("data-paused");
else entry.target.setAttribute("data-paused", "");
});
});
roots.forEach(function (el) { io.observe(el); });
})();
</script>
With reduced motion: The blue band rests on the left third and the copper one on the right, both still.
Running cost: Two bands slide with transform under a fixed mask; the dot pattern itself never repaints.
Two sheets of topographic contour lines, generated once from SVG noise, drift slowly past each other over a peach and lilac dawn gradient while a warm patch of sunlight moves along the top. The lines are drawn a single time and then only moved. It fits outdoor, travel, mapping and wellness sites with a soft, light palette.
<section class="ctr-root">
<div class="ctr-art" aria-hidden="true">
<div class="ctr-sun"></div>
<div class="ctr-sheet ctr-far"><svg viewBox="0 0 900 600" preserveAspectRatio="xMidYMid slice"><filter id="ctr-far-f" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB"><feTurbulence type="fractalNoise" baseFrequency="0.003" numOctaves="1" seed="11"/><feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0"/><feGaussianBlur stdDeviation="4"/><feComponentTransfer><feFuncA type="discrete" tableValues="0 .07 .14 .21 .28 .35 .42 .49 .56 .63 .70 .77 .84 .91 1"/></feComponentTransfer><feConvolveMatrix order="3" kernelMatrix="0 -1 0 -1 4 -1 0 -1 0" preserveAlpha="false"/><feComponentTransfer result="e"><feFuncA type="linear" slope="40"/></feComponentTransfer><feFlood flood-color="currentColor"/><feComposite operator="in" in2="e"/></filter><rect width="900" height="600" filter="url(#ctr-far-f)"/></svg></div>
<div class="ctr-sheet ctr-near"><svg viewBox="0 0 900 600" preserveAspectRatio="xMidYMid slice"><filter id="ctr-near-f" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB"><feTurbulence type="fractalNoise" baseFrequency="0.0042" numOctaves="2" seed="4"/><feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0"/><feGaussianBlur stdDeviation="4"/><feComponentTransfer><feFuncA type="discrete" tableValues="0 .05 .1 .15 .2 .25 .3 .35 .4 .45 .5 .55 .6 .65 .7 .75 .8 .85 .9 .95 1"/></feComponentTransfer><feConvolveMatrix order="3" kernelMatrix="0 -1 0 -1 4 -1 0 -1 0" preserveAlpha="false"/><feComponentTransfer result="e"><feFuncA type="linear" slope="40"/></feComponentTransfer><feFlood flood-color="currentColor"/><feComposite operator="in" in2="e"/></filter><rect width="900" height="600" filter="url(#ctr-near-f)"/></svg></div>
<div class="ctr-calm"></div>
</div>
<div class="ctr-hero">
<p class="ctr-eyebrow">Guided ridge walks</p>
<h1 class="ctr-title">Up before the light, home by lunch.</h1>
<p class="ctr-copy">Small-group dawn hikes on the high moor, with flasks of coffee at the summit cairn and a guide who knows every path.</p>
<a class="ctr-btn" href="#">Pick a morning</a>
</div>
</section>
<style>
.ctr-root{
/* Change these: */
--ctr-line: #7b4b63; /* contour line colour */
--ctr-strength: .38; /* how visible the near sheet of lines is */
--ctr-drift: 34s; /* time for one drift, there and back */
}
.ctr-root{position:relative;display:flex;align-items:center;min-height:620px;isolation:isolate;overflow:hidden;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
background:linear-gradient(160deg,#f8e9dc 0%,#f3dcd3 38%,#e7d6e3 72%,#dcd7ea 100%)}
.ctr-art{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.ctr-sun{position:absolute;left:12%;top:-20%;width:70%;height:80%;border-radius:50%;background:radial-gradient(closest-side,rgba(255,226,196,.9),transparent);will-change:transform;animation:ctr-sun calc(var(--ctr-drift) * 1.3) ease-in-out infinite alternate}
.ctr-sheet{position:absolute;inset:-12%;color:var(--ctr-line);will-change:transform}
.ctr-sheet svg{display:block;width:100%;height:100%}
.ctr-near{opacity:var(--ctr-strength);animation:ctr-near var(--ctr-drift) ease-in-out infinite alternate}
.ctr-far{opacity:calc(var(--ctr-strength) * .5);transform:scale(1.25);animation:ctr-far calc(var(--ctr-drift) * 1.5) ease-in-out infinite alternate}
.ctr-calm{position:absolute;left:50%;top:50%;width:min(820px,115%);height:430px;transform:translate(-50%,-50%);border-radius:50%;background:radial-gradient(closest-side,rgba(246,228,224,.85),rgba(246,228,224,.4) 60%,transparent)}
@keyframes ctr-near{from{transform:translate(-3%,-2%)}to{transform:translate(3%,2%)}}
@keyframes ctr-far{from{transform:scale(1.25) translate(2%,1%) rotate(0deg)}to{transform:scale(1.25) translate(-2%,-2%) rotate(3deg)}}
@keyframes ctr-sun{from{transform:translateX(0)}to{transform:translateX(30%)}}
.ctr-hero{position:relative;z-index:1;box-sizing:border-box;width:100%;max-width:720px;margin:0 auto;padding:112px 20px;text-align:center}
.ctr-hero > *{max-width:none}
.ctr-eyebrow{margin:0 0 18px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#7b3f5c}
.ctr-title{margin:0;font-size:clamp(2.1rem,5.4vw,3.6rem);line-height:1.06;letter-spacing:-.025em;font-weight:650;color:#2a1622;text-wrap:balance}
.ctr-copy{margin:20px auto 0;max-width:34em;font-size:clamp(1rem,1.6vw,1.125rem);line-height:1.6;color:#5b4250}
.ctr-btn{display:inline-flex;align-items:center;justify-content:center;min-height:46px;margin-top:32px;padding:0 24px;border-radius:999px;background:#3a1f30;color:#fbefe9;font-size:15px;font-weight:600;text-decoration:none;box-shadow:none;transition:transform .2s ease,background-color .2s ease}
.ctr-btn:hover{transform:translateY(-1px);background:#4e2a40}
.ctr-btn:focus-visible{outline:2px solid #7b3f5c;outline-offset:3px}
@media (max-width:640px){.ctr-hero{padding:88px 20px}}
.ctr-root[data-paused] *,.ctr-root[data-paused] *::before,.ctr-root[data-paused] *::after{animation-play-state:paused!important}
@media (prefers-reduced-motion: reduce){
.ctr-root *,.ctr-root *::before,.ctr-root *::after{animation:none!important;transition:none!important}
}
</style>
<script>
(function () {
// Pauses the animation while the section is off screen. Not needed for reduced motion.
if (!("IntersectionObserver" in window)) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
var roots = document.querySelectorAll(".ctr-root");
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) entry.target.removeAttribute("data-paused");
else entry.target.setAttribute("data-paused", "");
});
});
roots.forEach(function (el) { io.observe(el); });
})();
</script>
With reduced motion: Both contour sheets and the sunlight stay in their starting places as a still map.
Running cost: The SVG filter renders once; afterwards three layers move with transform only, though the first paint costs a few milliseconds.
A full-width photo hero treated like projected film: grain from an SVG noise tile jumps to a new spot several times a second, the picture weaves by a single pixel, and a faint light flicker comes and goes. The scrim darkens toward the text, so the headline and two buttons stay readable over a bright, foggy photo.
<section class="w4fgr">
<div class="w4fgr-film" aria-hidden="true">
<img class="w4fgr-photo" src="https://images.pexels.com/photos/29546724/pexels-photo-29546724.jpeg?auto=compress&cs=tinysrgb&w=1600" alt="A small wooden hut under tall pines in fog" width="1600" height="1067">
<div class="w4fgr-scrim"></div>
<div class="w4fgr-flick"></div>
</div>
<div class="w4fgr-inner">
<p class="w4fgr-kicker">Hollow Pine Huts</p>
<h2 class="w4fgr-title">One room, one stove, and the pines all around.</h2>
<p class="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 class="w4fgr-row">
<a href="#" class="w4fgr-btn main">See the huts</a>
<a href="#" class="w4fgr-btn alt">How to get here</a>
</div>
</div>
</section>
<style>
/* Change these: --w4fgr-grain (grain strength, 0 to 1), --w4fgr-flicker (projector flicker, 0 turns it off), --w4fgr-shade (r, g, b of the dark scrim), and the .6s on .w4fgr-film::after (grain speed). */
.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; } }
</style>
<script>
(function () {
/* Only pauses the grain, weave and flicker while the hero is off screen; delete it on short pages. */
var root = document.querySelector(".w4fgr");
if (!root) return;
new IntersectionObserver(function (en) { root.toggleAttribute("data-w4fgr-off", !en[0].isIntersecting); }).observe(root);
})();
</script>
With reduced motion: The photo, dark scrim and grain all stay on screen as one still frame; only the jumping, weave and flicker stop.
Running cost: Three small keyframe loops move or fade layers that are already painted, and an observer pauses them while the hero is out of view.
A near-black magazine section with warm, pale grain that reads like ink on uncoated stock, a lamp glow behind the headline, and a vignette that slowly deepens and lifts around the edges as if someone were breathing on the page. No photo is needed; the texture carries the mood behind a serif headline, byline and a two-column opening.
<section class="w4ink">
<div class="w4ink-air" aria-hidden="true">
<div class="w4ink-lamp"></div>
<div class="w4ink-vig"></div>
</div>
<div class="w4ink-inner">
<div class="w4ink-mast"><span>Margin Quarterly</span><span>Issue 9 · Autumn</span></div>
<h2 class="w4ink-title">The bakers who work <em>while the town sleeps</em></h2>
<p class="w4ink-by">Words by Tomas Reyna · 14 minute read</p>
<div class="w4ink-cols">
<p>At ten past two the first oven on Dock Road is already warm. Ines Varga lifts the cloth from forty loaves that have been rising since dusk and reads them by touch, the way her father did.</p>
<p>We spent a month of nights in three small bakeries, from the first mix to the van leaving at six, to learn what keeps people choosing a working life that runs opposite to everyone they love.</p>
</div>
<a href="#" class="w4ink-link">Read the essay</a>
</div>
</section>
<style>
/* Change these: --w4ink-bg (ink), --w4ink-lamp (r, g, b of the warm light and accent), --w4ink-grain (grain strength), and 9s on .w4ink-vig (how slowly the vignette breathes). */
.w4ink { --w4ink-bg: #131211; --w4ink-text: #ece4d6; --w4ink-lamp: 214, 168, 104; --w4ink-grain: .16;
position: relative; isolation: isolate; overflow: hidden; background: var(--w4ink-bg); color: var(--w4ink-text);
font-family: ui-sans-serif, system-ui, sans-serif; }
.w4ink-air { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.w4ink-lamp { position: absolute; left: 50%; top: 42%; width: 130%; height: 120%; margin: -60% 0 0 -65%; border-radius: 50%;
background: radial-gradient(closest-side, rgba(var(--w4ink-lamp), .2), rgba(var(--w4ink-lamp), .06) 55%, transparent); animation: w4ink-lamp 11s ease-in-out infinite alternate; }
.w4ink-vig { position: absolute; inset: -4%; background: radial-gradient(75% 70% at 50% 45%, transparent 40%, rgba(0, 0, 0, .82) 100%);
animation: w4ink-breathe 9s ease-in-out infinite alternate; }
.w4ink-air::after { content: ""; position: absolute; inset: -160px; opacity: var(--w4ink-grain); mix-blend-mode: screen;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='k'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.62' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 .96 0 0 0 0 .9 0 0 0 1.6 -.55'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23k)'/%3E%3C/svg%3E");
animation: w4ink-grain .8s steps(1) infinite; }
.w4ink[data-w4ink-off] .w4ink-air *, .w4ink[data-w4ink-off] .w4ink-air::after { animation-play-state: paused; }
@keyframes w4ink-grain { 0% { transform: translate(0, 0); } 12% { transform: translate(-71px, 33px); } 25% { transform: translate(48px, -86px); } 37% { transform: translate(-29px, -48px); }
50% { transform: translate(92px, 61px); } 62% { transform: translate(-104px, 14px); } 75% { transform: translate(17px, 99px); } 87% { transform: translate(66px, -21px); } }
@keyframes w4ink-breathe { 0% { opacity: .78; transform: scale(1.04); } 100% { opacity: 1; transform: scale(1); } }
@keyframes w4ink-lamp { 0% { opacity: .75; transform: translate(-3%, 1%); } 100% { opacity: 1; transform: translate(3%, -2%); } }
.w4ink-inner { max-width: 60rem; margin: 0 auto; padding: 76px 24px 84px; }
.w4ink-mast { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 6px 20px; padding-bottom: 14px; border-bottom: 1px solid rgba(236, 228, 214, .28);
font-size: 12px; letter-spacing: .2em; text-transform: uppercase; color: #c9bfae; }
.w4ink-title { margin: 44px 0 0; max-width: 44rem; font-family: Georgia, "Times New Roman", serif; font-size: clamp(38px, 6.4vw, 72px); font-weight: 400; line-height: 1.02; letter-spacing: -.015em; }
.w4ink-title em { color: rgb(var(--w4ink-lamp)); }
.w4ink-by { margin: 20px 0 0; font-size: 14px; color: #c9bfae; }
.w4ink-cols { margin-top: 36px; display: grid; gap: 22px; }
@media (min-width: 760px) { .w4ink-cols { grid-template-columns: 1fr 1fr; gap: 36px; } }
.w4ink-cols p { margin: 0; font-family: Georgia, "Times New Roman", serif; font-size: 17px; line-height: 1.7; color: #ddd4c4; }
.w4ink-cols p:first-child::first-letter { float: left; margin: 6px 10px 0 0; font-size: 58px; line-height: .8; color: var(--w4ink-text); }
.w4ink-link { display: inline-flex; align-items: center; gap: 10px; min-height: 44px; margin-top: 34px; color: var(--w4ink-text); font-size: 15px; font-weight: 600;
text-decoration: underline; text-underline-offset: 6px; text-decoration-color: rgba(var(--w4ink-lamp), .8); }
.w4ink-link:hover { color: #fff; }
.w4ink-link:focus-visible { outline: 2px solid var(--w4ink-text); outline-offset: 4px; border-radius: 2px; }
@media (prefers-reduced-motion: reduce) { .w4ink-air *, .w4ink-air::after { animation: none !important; } }
</style>
<script>
(function () {
/* Only pauses the grain, vignette and lamp loops while the section is off screen; delete it on short pages. */
var root = document.querySelector(".w4ink");
if (!root) return;
new IntersectionObserver(function (en) { root.toggleAttribute("data-w4ink-off", !en[0].isIntersecting); }).observe(root);
})();
</script>
With reduced motion: Grain, warm light and dark corners all remain as a single printed-looking frame, with no jumping or breathing.
Running cost: The grain layer jumps between eight fixed offsets and two soft gradients fade and drift slowly, all on transform and opacity, paused while off screen.
A deep sea-glass field of isometric cubes, drawn as one tiled SVG, with rings of pale light rolling outward from a point and catching only the top faces. A CSS mask shaped like the cube tops keeps the light on those faces, so the ripple reads like light across a tiled floor. Good for furniture, architecture and product pages.
<section class="icr-root">
<div class="icr-art" aria-hidden="true">
<div class="icr-cubes"></div>
<div class="icr-lit"><div class="icr-ring"></div><div class="icr-ring"></div><div class="icr-ring"></div><div class="icr-pool"></div></div>
<div class="icr-scrim"></div>
</div>
<div class="icr-hero">
<p class="icr-eyebrow">Modular shelving studio</p>
<h1 class="icr-title">Storage that clicks together like tiles.</h1>
<p class="icr-copy">Birch ply cubes in three depths, joined without tools and rearranged whenever the room changes.</p>
<a class="icr-btn" href="#">Plan your wall</a>
</div>
</section>
<style>
.icr-root{
/* Change these: */
--icr-light: 176,240,222; /* ripple light as r,g,b */
--icr-pulse: 7s; /* time for one ring to cross the field */
--icr-x: 26%; /* where the ripple starts, across */
--icr-y: 70%; /* where the ripple starts, down */
}
.icr-root{position:relative;display:flex;align-items:center;min-height:640px;isolation:isolate;overflow:hidden;background:#0c2724;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.icr-art{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.icr-cubes{position:absolute;inset:0;background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2756%27%20height%3D%2796.994%27%20viewBox%3D%270%200%2056%2096.994%27%20shape-rendering%3D%27geometricPrecision%27%3E%3Cpolygon%20points%3D%2728%2C0%2056%2C16.17%2028%2C32.33%200%2C16.17%27%20fill%3D%27%231f4f48%27%2F%3E%3Cpolygon%20points%3D%270%2C16.17%2028%2C32.33%2028%2C64.66%200%2C48.5%27%20fill%3D%27%23143a35%27%2F%3E%3Cpolygon%20points%3D%2728%2C32.33%2056%2C16.17%2056%2C48.5%2028%2C64.66%27%20fill%3D%27%230c2825%27%2F%3E%3Cpolygon%20points%3D%270%2C48.5%2028%2C64.66%200%2C80.83%20-28%2C64.66%27%20fill%3D%27%231f4f48%27%2F%3E%3Cpolygon%20points%3D%27-28%2C64.66%200%2C80.83%200%2C113.16%20-28%2C96.99%27%20fill%3D%27%23143a35%27%2F%3E%3Cpolygon%20points%3D%270%2C80.83%2028%2C64.66%2028%2C96.99%200%2C113.16%27%20fill%3D%27%230c2825%27%2F%3E%3Cpolygon%20points%3D%2756%2C48.5%2084%2C64.66%2056%2C80.83%2028%2C64.66%27%20fill%3D%27%231f4f48%27%2F%3E%3Cpolygon%20points%3D%2728%2C64.66%2056%2C80.83%2056%2C113.16%2028%2C96.99%27%20fill%3D%27%23143a35%27%2F%3E%3Cpolygon%20points%3D%2756%2C80.83%2084%2C64.66%2084%2C96.99%2056%2C113.16%27%20fill%3D%27%230c2825%27%2F%3E%3Cpolygon%20points%3D%270%2C-48.5%2028%2C-32.33%200%2C-16.17%20-28%2C-32.33%27%20fill%3D%27%231f4f48%27%2F%3E%3Cpolygon%20points%3D%27-28%2C-32.33%200%2C-16.17%200%2C16.17%20-28%2C0%27%20fill%3D%27%23143a35%27%2F%3E%3Cpolygon%20points%3D%270%2C-16.17%2028%2C-32.33%2028%2C0%200%2C16.17%27%20fill%3D%27%230c2825%27%2F%3E%3Cpolygon%20points%3D%2756%2C-48.5%2084%2C-32.33%2056%2C-16.17%2028%2C-32.33%27%20fill%3D%27%231f4f48%27%2F%3E%3Cpolygon%20points%3D%2728%2C-32.33%2056%2C-16.17%2056%2C16.17%2028%2C0%27%20fill%3D%27%23143a35%27%2F%3E%3Cpolygon%20points%3D%2756%2C-16.17%2084%2C-32.33%2084%2C0%2056%2C16.17%27%20fill%3D%27%230c2825%27%2F%3E%3Cpolygon%20points%3D%2728%2C96.99%2056%2C113.16%2028%2C129.33%200%2C113.16%27%20fill%3D%27%231f4f48%27%2F%3E%3Cpolygon%20points%3D%270%2C113.16%2028%2C129.33%2028%2C161.66%200%2C145.49%27%20fill%3D%27%23143a35%27%2F%3E%3Cpolygon%20points%3D%2728%2C129.33%2056%2C113.16%2056%2C145.49%2028%2C161.66%27%20fill%3D%27%230c2825%27%2F%3E%3Cpolygon%20points%3D%2728%2C0%2056%2C16.17%2028%2C32.33%200%2C16.17%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%270%2C16.17%2028%2C32.33%2028%2C64.66%200%2C48.5%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2728%2C32.33%2056%2C16.17%2056%2C48.5%2028%2C64.66%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%270%2C48.5%2028%2C64.66%200%2C80.83%20-28%2C64.66%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%27-28%2C64.66%200%2C80.83%200%2C113.16%20-28%2C96.99%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%270%2C80.83%2028%2C64.66%2028%2C96.99%200%2C113.16%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2756%2C48.5%2084%2C64.66%2056%2C80.83%2028%2C64.66%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2728%2C64.66%2056%2C80.83%2056%2C113.16%2028%2C96.99%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2756%2C80.83%2084%2C64.66%2084%2C96.99%2056%2C113.16%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%270%2C-48.5%2028%2C-32.33%200%2C-16.17%20-28%2C-32.33%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%27-28%2C-32.33%200%2C-16.17%200%2C16.17%20-28%2C0%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%270%2C-16.17%2028%2C-32.33%2028%2C0%200%2C16.17%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2756%2C-48.5%2084%2C-32.33%2056%2C-16.17%2028%2C-32.33%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2728%2C-32.33%2056%2C-16.17%2056%2C16.17%2028%2C0%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2756%2C-16.17%2084%2C-32.33%2084%2C0%2056%2C16.17%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2728%2C96.99%2056%2C113.16%2028%2C129.33%200%2C113.16%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%270%2C113.16%2028%2C129.33%2028%2C161.66%200%2C145.49%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3Cpolygon%20points%3D%2728%2C129.33%2056%2C113.16%2056%2C145.49%2028%2C161.66%27%20fill%3D%27none%27%20stroke%3D%27rgba(150%2C210%2C195%2C0.10)%27%20stroke-width%3D%27.6%27%2F%3E%3C%2Fsvg%3E");background-size:56px 96.994px}
.icr-lit{position:absolute;inset:0;-webkit-mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2756%27%20height%3D%2796.994%27%20viewBox%3D%270%200%2056%2096.994%27%20shape-rendering%3D%27geometricPrecision%27%3E%3Cpolygon%20points%3D%2728%2C0%2056%2C16.17%2028%2C32.33%200%2C16.17%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%270%2C48.5%2028%2C64.66%200%2C80.83%20-28%2C64.66%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%2756%2C48.5%2084%2C64.66%2056%2C80.83%2028%2C64.66%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%270%2C-48.5%2028%2C-32.33%200%2C-16.17%20-28%2C-32.33%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%2756%2C-48.5%2084%2C-32.33%2056%2C-16.17%2028%2C-32.33%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%2728%2C96.99%2056%2C113.16%2028%2C129.33%200%2C113.16%27%20fill%3D%27%23fff%27%2F%3E%3C%2Fsvg%3E");mask-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2756%27%20height%3D%2796.994%27%20viewBox%3D%270%200%2056%2096.994%27%20shape-rendering%3D%27geometricPrecision%27%3E%3Cpolygon%20points%3D%2728%2C0%2056%2C16.17%2028%2C32.33%200%2C16.17%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%270%2C48.5%2028%2C64.66%200%2C80.83%20-28%2C64.66%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%2756%2C48.5%2084%2C64.66%2056%2C80.83%2028%2C64.66%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%270%2C-48.5%2028%2C-32.33%200%2C-16.17%20-28%2C-32.33%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%2756%2C-48.5%2084%2C-32.33%2056%2C-16.17%2028%2C-32.33%27%20fill%3D%27%23fff%27%2F%3E%3Cpolygon%20points%3D%2728%2C96.99%2056%2C113.16%2028%2C129.33%200%2C113.16%27%20fill%3D%27%23fff%27%2F%3E%3C%2Fsvg%3E");-webkit-mask-size:56px 96.994px;mask-size:56px 96.994px}
.icr-ring{position:absolute;left:var(--icr-x);top:var(--icr-y);width:1800px;height:1800px;margin:-900px 0 0 -900px;border-radius:50%;opacity:0;will-change:transform,opacity;
background:radial-gradient(closest-side,transparent 72%,rgba(var(--icr-light),.0) 78%,rgba(var(--icr-light),.55) 88%,rgba(var(--icr-light),.9) 92%,rgba(var(--icr-light),0) 97%);
animation:icr-ring var(--icr-pulse) cubic-bezier(.25,.6,.4,1) infinite}
.icr-ring:nth-child(2){animation-delay:calc(var(--icr-pulse) / -3)}
.icr-ring:nth-child(3){animation-delay:calc(var(--icr-pulse) * -2 / 3)}
.icr-pool{position:absolute;left:var(--icr-x);top:var(--icr-y);width:700px;height:700px;margin:-350px 0 0 -350px;border-radius:50%;background:radial-gradient(closest-side,rgba(var(--icr-light),.35),transparent);animation:icr-pool calc(var(--icr-pulse) / 3) ease-in-out infinite}
.icr-scrim{position:absolute;inset:0;background:radial-gradient(60% 55% at 50% 48%,rgba(6,22,20,.86),rgba(6,22,20,.55) 60%,rgba(6,22,20,.2) 100%)}
@keyframes icr-ring{0%{transform:scale(.04);opacity:0}8%{opacity:1}70%{opacity:.55}100%{transform:scale(1);opacity:0}}
@keyframes icr-pool{0%,100%{opacity:.55;transform:scale(.9)}50%{opacity:1;transform:scale(1.05)}}
.icr-hero{position:relative;z-index:1;box-sizing:border-box;width:100%;max-width:720px;margin:0 auto;padding:112px 20px;text-align:center}
.icr-hero > *{max-width:none}
.icr-eyebrow{margin:0 0 18px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#a8e6d4}
.icr-title{margin:0;font-size:clamp(2.1rem,5.4vw,3.6rem);line-height:1.06;letter-spacing:-.025em;font-weight:650;color:#f2fbf8;text-wrap:balance}
.icr-copy{margin:20px auto 0;max-width:34em;font-size:clamp(1rem,1.6vw,1.125rem);line-height:1.6;color:#cfe4de}
.icr-btn{display:inline-flex;align-items:center;justify-content:center;min-height:46px;margin-top:32px;padding:0 24px;border-radius:10px;background:#b4ecdc;color:#0b2522;font-size:15px;font-weight:600;text-decoration:none;box-shadow:none;transition:transform .2s ease,background-color .2s ease}
.icr-btn:hover{transform:translateY(-1px);background:#c9f3e6}
.icr-btn:focus-visible{outline:2px solid #d6f6ec;outline-offset:3px}
@media (max-width:640px){.icr-hero{padding:88px 20px}}
.icr-root[data-paused] *,.icr-root[data-paused] *::before,.icr-root[data-paused] *::after{animation-play-state:paused!important}
@media (prefers-reduced-motion: reduce){
.icr-ring{opacity:.6;transform:scale(.42)}.icr-ring:nth-child(2){opacity:.3;transform:scale(.75)}.icr-ring:nth-child(3){opacity:0}
.icr-root *,.icr-root *::before,.icr-root *::after{animation:none!important;transition:none!important}
}
</style>
<script>
(function () {
// Pauses the animation while the section is off screen. Not needed for reduced motion.
if (!("IntersectionObserver" in window)) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
var roots = document.querySelectorAll(".icr-root");
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) entry.target.removeAttribute("data-paused");
else entry.target.setAttribute("data-paused", "");
});
});
roots.forEach(function (el) { io.observe(el); });
})();
</script>
With reduced motion: Two still rings of light rest on the cube tops near the starting point.
Running cost: Three scaling rings and a pulse inside one masked layer; masks are static, rings use transform and opacity only.
Three sheets of diagonal bands in sand and madder red slide sideways at different speeds and sizes, so the large front bands seem nearer than the faint ones behind. A soft light patch keeps the centre calm for the headline. It fits craft, textile and interior brands that want texture with gentle motion on a light page.
<section class="dsb-root">
<div class="dsb-art" aria-hidden="true">
<div class="dsb-layer dsb-far"></div>
<div class="dsb-layer dsb-mid"></div>
<div class="dsb-layer dsb-near"></div>
<div class="dsb-light"></div>
<div class="dsb-calm"></div>
</div>
<div class="dsb-hero">
<p class="dsb-eyebrow">Hand-loomed in small runs</p>
<h1 class="dsb-title">Rugs woven at the pace of the river.</h1>
<p class="dsb-copy">Flatweaves in undyed wool and madder red, each one finished by hand and numbered on the back.</p>
<a class="dsb-btn" href="#">See the spring weaves</a>
</div>
</section>
<style>
.dsb-root{
/* Change these: */
--dsb-ground: #efe3cf; /* background sand */
--dsb-band: 184,98,56; /* band colour as r,g,b */
--dsb-speed: 18s; /* time for the front layer to move one tile */
--dsb-near: 240px; /* front band tile size; the far layers scale from it */
}
.dsb-root{position:relative;display:flex;align-items:center;min-height:620px;isolation:isolate;overflow:hidden;background:var(--dsb-ground);font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.dsb-art{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.dsb-layer{position:absolute;top:-10%;bottom:-10%;will-change:transform}
.dsb-far{left:calc(var(--dsb-near) * -0.45);right:0;--t:calc(var(--dsb-near) * .45);background-size:var(--t) var(--t);
background-image:linear-gradient(45deg,rgba(var(--dsb-band),.07) 25%,transparent 25% 50%,rgba(var(--dsb-band),.07) 50% 75%,transparent 75%);animation:dsb-far calc(var(--dsb-speed) * 1.6) linear infinite}
.dsb-mid{left:calc(var(--dsb-near) * -0.7);right:0;--t:calc(var(--dsb-near) * .7);background-size:var(--t) var(--t);
background-image:linear-gradient(45deg,rgba(var(--dsb-band),.11) 15%,transparent 15% 50%,rgba(var(--dsb-band),.11) 50% 65%,transparent 65%);animation:dsb-mid calc(var(--dsb-speed) * 1.2) linear infinite}
.dsb-near{left:calc(var(--dsb-near) * -1);right:0;--t:var(--dsb-near);background-size:var(--t) var(--t);
background-image:linear-gradient(45deg,rgba(var(--dsb-band),.30) 0 1.2%,rgba(var(--dsb-band),.16) 1.2% 9%,rgba(255,250,240,.16) 9% 10.5%,transparent 10.5% 50%,rgba(var(--dsb-band),.30) 50% 51.2%,rgba(var(--dsb-band),.16) 51.2% 59%,rgba(255,250,240,.16) 59% 60.5%,transparent 60.5%);
animation:dsb-near var(--dsb-speed) linear infinite}
.dsb-light{position:absolute;inset:0;background:radial-gradient(70% 60% at 30% 20%,rgba(255,248,236,.55),transparent 70%),radial-gradient(80% 70% at 100% 100%,rgba(120,60,30,.14),transparent 70%)}
.dsb-calm{position:absolute;left:50%;top:50%;width:min(900px,110%);height:520px;transform:translate(-50%,-50%);border-radius:50%;background:radial-gradient(closest-side,rgba(239,227,207,.92),rgba(239,227,207,.6) 55%,transparent 100%)}
@keyframes dsb-far{to{transform:translateX(calc(var(--dsb-near) * .45))}}
@keyframes dsb-mid{to{transform:translateX(calc(var(--dsb-near) * .7))}}
@keyframes dsb-near{to{transform:translateX(var(--dsb-near))}}
.dsb-hero{position:relative;z-index:1;box-sizing:border-box;width:100%;max-width:720px;margin:0 auto;padding:112px 20px;text-align:center}
.dsb-hero > *{max-width:none}
.dsb-eyebrow{margin:0 0 18px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#8a3f1c}
.dsb-title{margin:0;font-size:clamp(2.1rem,5.4vw,3.6rem);line-height:1.06;letter-spacing:-.025em;font-weight:650;color:#2b1a10;text-wrap:balance}
.dsb-copy{margin:20px auto 0;max-width:34em;font-size:clamp(1rem,1.6vw,1.125rem);line-height:1.6;color:#5a4436}
.dsb-btn{display:inline-flex;align-items:center;justify-content:center;min-height:46px;margin-top:32px;padding:0 24px;border-radius:4px;background:#2b1a10;color:#f8efe2;font-size:15px;font-weight:600;text-decoration:none;box-shadow:none;transition:transform .2s ease,background-color .2s ease}
.dsb-btn:hover{transform:translateY(-1px);background:#43291a}
.dsb-btn:focus-visible{outline:2px solid #8a3f1c;outline-offset:3px}
@media (max-width:640px){.dsb-hero{padding:88px 20px}}
.dsb-root[data-paused] *,.dsb-root[data-paused] *::before,.dsb-root[data-paused] *::after{animation-play-state:paused!important}
@media (prefers-reduced-motion: reduce){
.dsb-root *,.dsb-root *::before,.dsb-root *::after{animation:none!important;transition:none!important}
}
</style>
<script>
(function () {
// Pauses the animation while the section is off screen. Not needed for reduced motion.
if (!("IntersectionObserver" in window)) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
var roots = document.querySelectorAll(".dsb-root");
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) entry.target.removeAttribute("data-paused");
else entry.target.setAttribute("data-paused", "");
});
});
roots.forEach(function (el) { io.observe(el); });
})();
</script>
With reduced motion: All three band layers stop in place, leaving a still woven pattern with the same depth.
Running cost: Three tiled layers move with translateX on the compositor; nothing repaints while they slide.
A lamp on a dark headland turns two soft beams of warm light through banks of slate-blue fog, with a faint glint crossing the sea each time a beam passes. The beams are one rotating conic gradient that fades with distance. It suits maritime, travel or night services that want slow, steady motion behind a left-aligned headline.
<section class="lhb-root">
<div class="lhb-art" aria-hidden="true">
<div class="lhb-sky"></div>
<div class="lhb-fog lhb-fog-c"></div>
<div class="lhb-beams"></div>
<div class="lhb-fog lhb-fog-a"></div>
<div class="lhb-sea"></div>
<div class="lhb-glint"></div>
<div class="lhb-land"></div>
<div class="lhb-tower"></div>
<div class="lhb-lamp"></div>
<div class="lhb-fog lhb-fog-b"></div>
<div class="lhb-veil"></div>
</div>
<div class="lhb-hero">
<p class="lhb-eyebrow">Harbour pilots, est. 1974</p>
<h1 class="lhb-title">Bringing you in, whatever the weather.</h1>
<p class="lhb-copy">Licensed pilots guide cargo and charter boats through the narrows day and night, with radio cover every hour of the tide.</p>
<a class="lhb-btn" href="#">Request a pilot</a>
</div>
</section>
<style>
.lhb-root{
/* Change these: */
--lhb-beam: 255,236,196; /* beam colour as r,g,b */
--lhb-turn: 16s; /* one full turn of the lamp */
--lhb-fog: 22s; /* drift time of the fog banks */
--lhb-x: 84%; /* lamp position across */
--lhb-y: 46%; /* lamp position down */
}
.lhb-root{position:relative;display:flex;align-items:center;min-height:640px;isolation:isolate;overflow:hidden;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
background:linear-gradient(180deg,#0c141d 0%,#16222e 45%,#1d2a36 62%,#0e161e 74%,#0a1016 100%)}
.lhb-art{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.lhb-sky{position:absolute;inset:0;background:radial-gradient(50% 45% at var(--lhb-x) var(--lhb-y),rgba(120,150,170,.22),transparent 70%)}
.lhb-beams{position:absolute;left:var(--lhb-x);top:var(--lhb-y);width:3000px;height:3000px;margin:-1500px 0 0 -1500px;border-radius:50%;will-change:transform;
background:conic-gradient(from 0deg,
rgba(var(--lhb-beam),0) 0deg,rgba(var(--lhb-beam),.05) 3deg,rgba(var(--lhb-beam),.30) 9deg,rgba(var(--lhb-beam),.42) 12deg,rgba(var(--lhb-beam),.30) 15deg,rgba(var(--lhb-beam),.05) 21deg,rgba(var(--lhb-beam),0) 25deg,
rgba(var(--lhb-beam),0) 180deg,rgba(var(--lhb-beam),.04) 183deg,rgba(var(--lhb-beam),.24) 189deg,rgba(var(--lhb-beam),.34) 192deg,rgba(var(--lhb-beam),.24) 195deg,rgba(var(--lhb-beam),.04) 201deg,rgba(var(--lhb-beam),0) 205deg);
-webkit-mask-image:radial-gradient(circle,#000 0,rgba(0,0,0,.85) 8%,rgba(0,0,0,.35) 26%,transparent 46%);mask-image:radial-gradient(circle,#000 0,rgba(0,0,0,.85) 8%,rgba(0,0,0,.35) 26%,transparent 46%);
transform:rotate(200deg);animation:lhb-turn var(--lhb-turn) linear infinite}
.lhb-fog{position:absolute;left:-30%;width:160%;border-radius:50%;will-change:transform}
.lhb-fog-a{top:28%;height:46%;background:radial-gradient(closest-side,rgba(170,190,205,.20),transparent);animation:lhb-drift var(--lhb-fog) ease-in-out infinite alternate}
.lhb-fog-b{top:50%;height:36%;background:radial-gradient(closest-side,rgba(150,172,190,.18),transparent);animation:lhb-drift var(--lhb-fog) ease-in-out -9s infinite alternate-reverse}
.lhb-fog-c{top:8%;height:40%;left:-10%;width:90%;background:radial-gradient(closest-side,rgba(140,160,180,.12),transparent);animation:lhb-drift calc(var(--lhb-fog) * 1.4) ease-in-out -4s infinite alternate}
.lhb-sea{position:absolute;left:0;right:0;top:74%;bottom:0;background:linear-gradient(180deg,#121c25 0%,#0a1117 100%)}
.lhb-glint{position:absolute;left:50%;right:-10%;top:74%;height:10%;background:radial-gradient(40% 70% at 70% 0%,rgba(var(--lhb-beam),.16),transparent 70%);animation:lhb-glint calc(var(--lhb-turn) / 2) ease-in-out infinite}
.lhb-land{position:absolute;left:calc(var(--lhb-x) - 30%);width:60%;top:calc(var(--lhb-y) + 7%);height:60%;left:calc(var(--lhb-x) - 45%);width:80%;border-radius:60% 40% 0 0/22% 18% 0 0;background:#070c11;-webkit-mask-image:linear-gradient(90deg,transparent,#000 35%);mask-image:linear-gradient(90deg,transparent,#000 35%)}
.lhb-tower{position:absolute;left:var(--lhb-x);top:var(--lhb-y);width:14px;height:12%;margin-left:-7px;background:linear-gradient(90deg,#0b1218,#16212b 60%,#0b1218);clip-path:polygon(20% 0,80% 0,100% 100%,0 100%)}
.lhb-lamp{position:absolute;left:var(--lhb-x);top:var(--lhb-y);width:120px;height:120px;margin:-60px 0 0 -60px;border-radius:50%;
background:radial-gradient(closest-side,rgba(255,250,235,1) 0,rgba(var(--lhb-beam),.85) 8%,rgba(var(--lhb-beam),.25) 30%,transparent 100%);animation:lhb-flare calc(var(--lhb-turn) / 2) ease-in-out infinite}
.lhb-veil{position:absolute;inset:0;background:linear-gradient(90deg,rgba(9,14,20,.55) 0%,rgba(9,14,20,.25) 45%,transparent 70%)}
@keyframes lhb-turn{from{transform:rotate(200deg)}to{transform:rotate(560deg)}}
@keyframes lhb-drift{from{transform:translateX(-8%)}to{transform:translateX(8%)}}
@keyframes lhb-flare{0%,100%{opacity:.75;transform:scale(.9)}50%{opacity:1;transform:scale(1.1)}}
@keyframes lhb-glint{0%,100%{opacity:.3}50%{opacity:1}}
@media (max-width:640px){.lhb-root{--lhb-x:80%;--lhb-y:22%}}
.lhb-hero{position:relative;z-index:1;box-sizing:border-box;width:100%;max-width:1120px;margin:0 auto;padding:112px 20px;text-align:left}
.lhb-hero > *{max-width:560px}
.lhb-eyebrow{margin:0 0 18px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#f3d9a4}
.lhb-title{margin:0;font-size:clamp(2.1rem,5.4vw,3.6rem);line-height:1.06;letter-spacing:-.025em;font-weight:650;color:#f6f2ea;text-wrap:balance}
.lhb-copy{margin:20px 0 0;max-width:34em;font-size:clamp(1rem,1.6vw,1.125rem);line-height:1.6;color:#d3dbe2}
.lhb-btn{display:inline-flex;align-items:center;justify-content:center;min-height:46px;margin-top:32px;padding:0 24px;border-radius:6px;background:#f1d9a8;color:#14202b;font-size:15px;font-weight:600;text-decoration:none;box-shadow:none;transition:transform .2s ease,background-color .2s ease}
.lhb-btn:hover{transform:translateY(-1px);background:#f7e4bd}
.lhb-btn:focus-visible{outline:2px solid #f8e7c5;outline-offset:3px}
@media (max-width:640px){.lhb-hero{padding:88px 20px}}
.lhb-root[data-paused] *,.lhb-root[data-paused] *::before,.lhb-root[data-paused] *::after{animation-play-state:paused!important}
@media (prefers-reduced-motion: reduce){
.lhb-beams{transform:rotate(206deg)}
.lhb-root *,.lhb-root *::before,.lhb-root *::after{animation:none!important;transition:none!important}
}
</style>
<script>
(function () {
// Pauses the animation while the section is off screen. Not needed for reduced motion.
if (!("IntersectionObserver" in window)) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
var roots = document.querySelectorAll(".lhb-root");
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) entry.target.removeAttribute("data-paused");
else entry.target.setAttribute("data-paused", "");
});
});
roots.forEach(function (el) { io.observe(el); });
})();
</script>
With reduced motion: The lamp stays lit with one beam held pointing up and to the left over still fog.
Running cost: One large rotating layer plus three drifting fog layers, all transform or opacity; no script loop.
A close view of oatmeal linen, with crossing threads, uneven slubs and a soft fold, while a warm lamp glow circles slowly over the cloth and brings the weave forward wherever it lands. The texture comes from layered gradients and generated noise, so there is no image to load. It suits bedding, clothing, interiors and slow living brands.
<section class="lnw">
<div class="lnw-fx" aria-hidden="true"><div class="lnw-weave"></div><div class="lnw-slub"></div><div class="lnw-crease"></div><div class="lnw-lamp"></div><div class="lnw-lamp lnw-lamp2"></div><div class="lnw-vignette"></div></div>
<div class="lnw-hero"><p class="lnw-eyebrow">Flax and Fold</p><h1 class="lnw-title">Washed linen bedding that softens every year</h1><p class="lnw-copy">Stone washed in a small mill, cut in our workshop and shipped folded in paper with no plastic anywhere.</p><a class="lnw-btn" href="#">Shop the sheets</a></div>
</section>
<style>
/* Change these: cloth colour, lamp colour, seconds for one lamp circuit, thread spacing */
.lnw{--lnw-cloth:#e0d4bf;--lnw-lamp:rgba(255,228,176,.95);--lnw-speed:24s;--lnw-thread:3px}
.lnw{position:relative;isolation:isolate;overflow:hidden;min-height:560px;display:flex;align-items:center;background:var(--lnw-cloth);color:#2b241c;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.lnw-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.lnw-fx *{position:absolute;pointer-events:none}
.lnw-hero{position:relative;z-index:1;width:100%;max-width:1120px;margin:0 auto;padding:72px 24px;box-sizing:border-box}
.lnw-eyebrow{margin:0 0 14px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.lnw-title{margin:0;max-width:15ch;font-size:clamp(34px,5.6vw,60px);line-height:1.04;letter-spacing:-.025em;font-weight:650}
.lnw-copy{margin:20px 0 30px;max-width:44ch;font-size:clamp(16px,1.6vw,18px);line-height:1.6}
.lnw-btn{display:inline-block;padding:13px 24px;border-radius:999px;font-size:15px;font-weight:600;text-decoration:none;transition:transform .2s ease}
.lnw-btn:hover{transform:translateY(-1px)}
.lnw-btn:focus-visible{outline:3px solid currentColor;outline-offset:4px}
.lnw[data-paused] *,.lnw[data-paused] *::before,.lnw[data-paused] *::after{animation-play-state:paused!important}
.lnw[data-reduced] *,.lnw[data-reduced] *::before,.lnw[data-reduced] *::after{animation:none!important}
@media (prefers-reduced-motion:reduce){.lnw *,.lnw *::before,.lnw *::after{animation:none!important}}
.lnw-weave{inset:0;background:repeating-linear-gradient(0deg,rgba(90,68,40,.13) 0,rgba(90,68,40,.13) 1px,rgba(0,0,0,0) 1px,rgba(0,0,0,0) var(--lnw-thread)),repeating-linear-gradient(90deg,rgba(255,250,238,.22) 0,rgba(255,250,238,.22) 1px,rgba(0,0,0,0) 1px,rgba(0,0,0,0) var(--lnw-thread)),repeating-linear-gradient(90deg,rgba(90,68,40,.09) 0,rgba(90,68,40,.09) 1px,rgba(0,0,0,0) 1px,rgba(0,0,0,0) calc(var(--lnw-thread) * 1.7))}
.lnw-slub{inset:0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.006 .32' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .24 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.28 .008' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .16 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"),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='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .18 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");mix-blend-mode:multiply}
.lnw-crease{inset:0;background:linear-gradient(112deg,rgba(255,255,255,0) 50%,rgba(255,250,240,.22) 57%,rgba(70,50,28,.14) 60%,rgba(70,50,28,0) 67%)}
.lnw-lamp{width:110%;aspect-ratio:1;left:22%;top:-48%;background:radial-gradient(closest-side,var(--lnw-lamp),rgba(255,228,176,.35) 45%,rgba(255,228,176,0) 78%);mix-blend-mode:soft-light;opacity:1;transform:translate(10%,-6%);animation:lnw-orbit var(--lnw-speed) ease-in-out infinite;animation-delay:calc(var(--lnw-speed) * -.2)}
.lnw-lamp2{mix-blend-mode:screen;opacity:.32}
.lnw-vignette{inset:0;background:radial-gradient(120% 100% at 62% 35%,rgba(60,42,22,0) 50%,rgba(60,42,22,.3))}
.lnw-eyebrow{color:#6b4a26}
.lnw-copy{color:#3e3428}
.lnw-btn{background:#2b241c;color:#f4ede1}
@keyframes lnw-orbit{0%,100%{transform:translate(10%,-6%)}33%{transform:translate(-12%,10%)}66%{transform:translate(18%,14%)}}
</style>
<script>
(function () {
// Pauses the animation while the block is off screen, and stops it for reduced motion.
var roots = document.querySelectorAll(".lnw");
var still = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
roots.forEach(function (root) {
if (still) { root.setAttribute("data-reduced", ""); return; }
if (!("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) root.removeAttribute("data-paused");
else root.setAttribute("data-paused", "");
}).observe(root);
});
})();
</script>
With reduced motion: The lamp stays over the upper right of the cloth, so the weave is lit there and shaded toward the edges.
Running cost: One glow layer moves by transform with an overlay blend; the weave and noise are painted once.
A dusk-coloured floor grid tilted in 3D with CSS perspective, rolling slowly toward the viewer while a warm pool of light slides from side to side across the floor. A soft sun sits on the horizon and the far lines fade into haze, so the headline above has a calm dark sky to sit on. Everything is CSS: one rotated plane, one moving line layer and one gliding glow. On phones the horizon sits lower.
<section class="pfg-root">
<div class="pfg-art" aria-hidden="true">
<div class="pfg-sky"></div>
<div class="pfg-sun"></div>
<div class="pfg-haze"></div>
<div class="pfg-stage"><div class="pfg-plane"><div class="pfg-lines"></div><div class="pfg-glow"></div></div></div>
<div class="pfg-shade"></div>
</div>
<div class="pfg-hero">
<p class="pfg-eyebrow">Night shift synth rentals</p>
<h1 class="pfg-title">Analog gear, delivered before soundcheck.</h1>
<p class="pfg-copy">Modular racks, drum machines and vintage keys, tested the morning they ship and collected after the show.</p>
<a class="pfg-btn" href="#">Browse tonight's racks</a>
</div>
</section>
<style>
.pfg-root{
/* Change these: */
--pfg-line: rgba(255,178,128,.42); /* grid line colour */
--pfg-glow: rgba(255,150,100,.9); /* travelling glow colour */
--pfg-cell: 46px; /* grid cell size */
--pfg-speed: 3.2s; /* time to roll one cell toward you */
--pfg-sweep: 14s; /* time for the glow to cross and return */
}
@property --pfg-heat{syntax:"<number>";inherits:false;initial-value:.7}
.pfg-root{position:relative;display:flex;align-items:flex-start;min-height:640px;isolation:isolate;overflow:hidden;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
background:linear-gradient(180deg,#070b16 0%,#0e1226 34%,#241a36 52%,#4a2a3c 58%,#1a1020 66%,#0a0710 100%)}
.pfg-art{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.pfg-sky{position:absolute;inset:0;background:radial-gradient(60% 40% at 50% 58%,rgba(255,150,110,.22),transparent 70%),radial-gradient(90% 60% at 50% 0%,rgba(70,90,160,.18),transparent 70%)}
.pfg-sun{position:absolute;left:50%;top:62%;width:min(1100px,140%);height:260px;transform:translate(-50%,-55%);border-radius:50%;
background:radial-gradient(closest-side,rgba(255,196,150,calc(var(--pfg-heat)*.75)),rgba(255,120,90,calc(var(--pfg-heat)*.22)) 55%,transparent 100%);animation:pfg-breathe 9s ease-in-out infinite alternate}
.pfg-stage{position:absolute;left:0;right:0;top:62%;bottom:0;perspective:520px;perspective-origin:50% 0;
-webkit-mask-image:linear-gradient(180deg,transparent 0%,#000 38%);mask-image:linear-gradient(180deg,transparent 0%,#000 38%)}
.pfg-plane{position:absolute;left:-150%;right:-150%;top:0;height:420%;transform-origin:50% 0;transform:rotateX(76deg);overflow:hidden}
.pfg-lines{position:absolute;left:0;right:0;top:calc(var(--pfg-cell) * -1);bottom:0;will-change:transform;
background-image:linear-gradient(var(--pfg-line) 2px,transparent 2px),linear-gradient(90deg,var(--pfg-line) 2px,transparent 2px);
background-size:var(--pfg-cell) var(--pfg-cell);background-position:50% 0;animation:pfg-roll var(--pfg-speed) linear infinite}
.pfg-glow{position:absolute;left:50%;top:0;width:700px;height:2200px;margin-left:-350px;border-radius:50%;mix-blend-mode:screen;will-change:transform;
background:radial-gradient(closest-side,var(--pfg-glow),rgba(255,120,80,.18) 45%,transparent 75%);animation:pfg-sweep var(--pfg-sweep) ease-in-out infinite alternate;transform:translateX(-26vw)}
.pfg-haze{position:absolute;left:0;right:0;top:50%;height:22%;background:linear-gradient(180deg,transparent,rgba(255,170,140,.10) 45%,rgba(40,20,40,.0));filter:none}
.pfg-shade{position:absolute;inset:0;background:radial-gradient(120% 70% at 50% 100%,transparent 40%,rgba(5,4,10,.65) 100%)}
@keyframes pfg-roll{to{transform:translateY(var(--pfg-cell))}}
@keyframes pfg-sweep{0%{transform:translateX(-26vw)}100%{transform:translateX(26vw)}}
@keyframes pfg-breathe{0%{--pfg-heat:.7;opacity:.9}100%{--pfg-heat:1;opacity:1}}
.pfg-hero{position:relative;z-index:1;box-sizing:border-box;width:100%;max-width:720px;margin:0 auto;padding:84px 20px 290px;text-align:center}
.pfg-hero > *{max-width:none}
.pfg-eyebrow{margin:0 0 18px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#ffc9a8}
.pfg-title{margin:0;font-size:clamp(2.1rem,5.4vw,3.6rem);line-height:1.06;letter-spacing:-.025em;font-weight:650;color:#fff7f0;text-wrap:balance}
.pfg-copy{margin:20px auto 0;max-width:34em;font-size:clamp(1rem,1.6vw,1.125rem);line-height:1.6;color:#e9dcd6}
.pfg-btn{display:inline-flex;align-items:center;justify-content:center;min-height:46px;margin-top:32px;padding:0 24px;border-radius:999px;background:#ffb48a;color:#24121a;font-size:15px;font-weight:600;text-decoration:none;box-shadow:0 10px 30px -10px rgba(255,150,100,.6);transition:transform .2s ease,background-color .2s ease}
.pfg-btn:hover{transform:translateY(-1px);background:#ffc3a0}
.pfg-btn:focus-visible{outline:2px solid #ffd9c2;outline-offset:3px}
@media (max-width:640px){.pfg-hero{padding:64px 20px 250px}}
.pfg-root[data-paused] *,.pfg-root[data-paused] *::before,.pfg-root[data-paused] *::after{animation-play-state:paused!important}
@media (prefers-reduced-motion: reduce){
.pfg-glow{transform:translateX(-12vw)}
.pfg-root *,.pfg-root *::before,.pfg-root *::after{animation:none!important;transition:none!important}
}
</style>
<script>
(function () {
// Pauses the animation while the section is off screen. Not needed for reduced motion.
if (!("IntersectionObserver" in window)) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
var roots = document.querySelectorAll(".pfg-root");
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) entry.target.removeAttribute("data-paused");
else entry.target.setAttribute("data-paused", "");
});
});
roots.forEach(function (el) { io.observe(el); });
})();
</script>
With reduced motion: The grid holds still with the glow resting just left of centre and the horizon sun at full warmth.
Running cost: Two transformed layers and one opacity fade on the compositor; no JavaScript beyond an off-screen pause.
Street lights and passing cars are seen through a fluted glass window at night: amber, white and red glows slide sideways at different speeds, broken into tall streaks by the vertical reeds. A navy shade on the left holds the text. It fits bars, restaurants, hotels and evening events that want a city mood without a video.
<section class="rdg">
<div class="rdg-fx" aria-hidden="true"><div class="rdg-lights"><div class="rdg-l rdg-l1"></div><div class="rdg-l rdg-l2"></div><div class="rdg-l rdg-l3"></div><div class="rdg-l rdg-l4"></div><div class="rdg-l rdg-l5"></div><div class="rdg-l rdg-l6"></div></div><div class="rdg-reeds"></div><div class="rdg-scrim"></div></div>
<div class="rdg-hero"><p class="rdg-eyebrow">Lantern Street Wine Bar</p><h1 class="rdg-title">Natural wine and small plates behind the fluted glass</h1><p class="rdg-copy">Open from five until late, six nights a week. Walk-ins welcome at the counter; tables can be held for groups of six or more.</p><a class="rdg-btn" href="#">Hold a table</a></div>
</section>
<style>
/* Change these: night colour, width of one glass reed, base seconds for a passing light, main lamp colour */
.rdg{--rdg-night:#0c1626;--rdg-reed:22px;--rdg-speed:14s;--rdg-amber:#ffb45e}
.rdg{position:relative;isolation:isolate;overflow:hidden;min-height:560px;display:flex;align-items:center;background:linear-gradient(180deg,#0f1b2e,var(--rdg-night));color:#f4efe6;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.rdg-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.rdg-fx *{position:absolute;pointer-events:none}
.rdg-hero{position:relative;z-index:1;width:100%;max-width:1120px;margin:0 auto;padding:72px 24px;box-sizing:border-box}
.rdg-eyebrow{margin:0 0 14px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.rdg-title{margin:0;max-width:15ch;font-size:clamp(34px,5.6vw,60px);line-height:1.04;letter-spacing:-.025em;font-weight:650}
.rdg-copy{margin:20px 0 30px;max-width:44ch;font-size:clamp(16px,1.6vw,18px);line-height:1.6}
.rdg-btn{display:inline-block;padding:13px 24px;border-radius:999px;font-size:15px;font-weight:600;text-decoration:none;transition:transform .2s ease}
.rdg-btn:hover{transform:translateY(-1px)}
.rdg-btn:focus-visible{outline:3px solid currentColor;outline-offset:4px}
.rdg[data-paused] *,.rdg[data-paused] *::before,.rdg[data-paused] *::after{animation-play-state:paused!important}
.rdg[data-reduced] *,.rdg[data-reduced] *::before,.rdg[data-reduced] *::after{animation:none!important}
@media (prefers-reduced-motion:reduce){.rdg *,.rdg *::before,.rdg *::after{animation:none!important}}
.rdg-lights{inset:0;-webkit-mask-image:repeating-linear-gradient(90deg,rgba(0,0,0,.3) 0,#000 calc(var(--rdg-reed) * .45),rgba(0,0,0,.3) var(--rdg-reed));mask-image:repeating-linear-gradient(90deg,rgba(0,0,0,.3) 0,#000 calc(var(--rdg-reed) * .45),rgba(0,0,0,.3) var(--rdg-reed))}
.rdg-l{left:-22%;width:18%;height:80%;border-radius:50%;animation:rdg-pass var(--rdg-speed) linear infinite}
.rdg-l1{top:8%;background:radial-gradient(closest-side,var(--rdg-amber),rgba(255,180,94,0));animation-duration:calc(var(--rdg-speed) * .8);animation-delay:calc(var(--rdg-speed) * -.35)}
.rdg-l2{top:22%;height:70%;background:radial-gradient(closest-side,rgba(255,244,222,.9),rgba(255,244,222,0));animation-duration:calc(var(--rdg-speed) * 1.25);animation-delay:calc(var(--rdg-speed) * -.6)}
.rdg-l3{top:30%;width:12%;background:radial-gradient(closest-side,rgba(255,86,64,.85),rgba(255,86,64,0));animation-direction:reverse;animation-delay:calc(var(--rdg-speed) * -.25)}
.rdg-l4{top:2%;width:22%;background:radial-gradient(closest-side,rgba(255,170,80,.75),rgba(255,170,80,0));animation-direction:reverse;animation-duration:calc(var(--rdg-speed) * 1.6);animation-delay:calc(var(--rdg-speed) * -1.1)}
.rdg-l5{top:-10%;width:34%;height:100%;background:radial-gradient(closest-side,rgba(150,210,220,.35),rgba(150,210,220,0));animation-duration:calc(var(--rdg-speed) * 3);animation-delay:calc(var(--rdg-speed) * -1.4)}
.rdg-l6{top:14%;width:14%;background:radial-gradient(closest-side,rgba(255,200,120,.9),rgba(255,200,120,0));animation-duration:calc(var(--rdg-speed) * .65);animation-delay:calc(var(--rdg-speed) * -.1)}
.rdg-reeds{inset:0;background:repeating-linear-gradient(90deg,rgba(255,255,255,.09) 0,rgba(255,255,255,0) 3px,rgba(0,0,0,0) calc(var(--rdg-reed) * .55),rgba(0,0,0,.32) calc(var(--rdg-reed) - 2px),rgba(255,255,255,.12) var(--rdg-reed))}
.rdg-scrim{inset:0;background:linear-gradient(90deg,rgba(9,16,29,.86) 0,rgba(9,16,29,.6) 42%,rgba(9,16,29,.08) 78%)}
.rdg-eyebrow{color:var(--rdg-amber)}
.rdg-copy{color:#d5d0c7}
.rdg-btn{background:var(--rdg-amber);color:#1b1206}
@keyframes rdg-pass{from{transform:translateX(0)}to{transform:translateX(720%)}}
@media (max-width:700px){.rdg-scrim{background:linear-gradient(180deg,rgba(9,16,29,.55),rgba(9,16,29,.78))}}
</style>
<script>
(function () {
// Pauses the animation while the block is off screen, and stops it for reduced motion.
var roots = document.querySelectorAll(".rdg");
var still = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
roots.forEach(function (root) {
if (still) { root.setAttribute("data-reduced", ""); return; }
if (!("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) root.removeAttribute("data-paused");
else root.setAttribute("data-paused", "");
}).observe(root);
});
})();
</script>
With reduced motion: The lights hold their places behind the glass as still streaks of amber, white and red.
Running cost: Six blurred gradient shapes move by transform under a static mask; no filters are animated.
Seen from beneath the glass of a copier, a thin mint light bar glides up and down under a dim document, lifting its lines into view as it passes. It fits archiving services, document tools, print shops and any product about turning paper into files. On narrow screens the sheet slides down and to the side so the heading never sits on the brightest line.
<section class="cpy">
<div class="cpy-fx" aria-hidden="true"><div class="cpy-page"><div class="cpy-head"></div><div class="cpy-lines"></div><div class="cpy-stamp"></div></div><div class="cpy-bar"></div><div class="cpy-dust"></div><div class="cpy-sheen"></div></div>
<div class="cpy-hero"><p class="cpy-eyebrow">Paper Trail Archive</p><h1 class="cpy-title">Boxes of old records, turned into files you can search</h1><p class="cpy-copy">We scan deeds, ledgers and family letters at full resolution, then return every original page in the order it arrived.</p><a class="cpy-btn" href="#">Get a scanning quote</a></div>
</section>
<style>
/* Change these: glass colour, colour of the light bar, seconds for one pass, text colour */
.cpy{--cpy-glass:#121618;--cpy-light:rgba(196,255,238,.9);--cpy-speed:6.5s;--cpy-ink:#eef3f2}
.cpy{position:relative;isolation:isolate;overflow:hidden;min-height:560px;display:flex;align-items:center;background:radial-gradient(90% 80% at 72% 50%,#1e2528 0,var(--cpy-glass) 70%);color:var(--cpy-ink);font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
.cpy-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.cpy-fx *{position:absolute;pointer-events:none}
.cpy-hero{position:relative;z-index:1;width:100%;max-width:1120px;margin:0 auto;padding:72px 24px;box-sizing:border-box}
.cpy-eyebrow{margin:0 0 14px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.cpy-title{margin:0;max-width:15ch;font-size:clamp(34px,5.6vw,60px);line-height:1.04;letter-spacing:-.025em;font-weight:650}
.cpy-copy{margin:20px 0 30px;max-width:44ch;font-size:clamp(16px,1.6vw,18px);line-height:1.6}
.cpy-btn{display:inline-block;padding:13px 24px;border-radius:999px;font-size:15px;font-weight:600;text-decoration:none;transition:transform .2s ease}
.cpy-btn:hover{transform:translateY(-1px)}
.cpy-btn:focus-visible{outline:3px solid currentColor;outline-offset:4px}
.cpy[data-paused] *,.cpy[data-paused] *::before,.cpy[data-paused] *::after{animation-play-state:paused!important}
.cpy[data-reduced] *,.cpy[data-reduced] *::before,.cpy[data-reduced] *::after{animation:none!important}
@media (prefers-reduced-motion:reduce){.cpy *,.cpy *::before,.cpy *::after{animation:none!important}}
.cpy-page{width:min(44%,500px);aspect-ratio:.77;right:7%;top:50%;transform:translateY(-50%) rotate(2.5deg);background:#272e31;box-shadow:0 30px 60px rgba(0,0,0,.45)}
.cpy-head{left:11%;top:9%;width:46%;height:5%;background:#384246}
.cpy-lines{left:11%;right:11%;top:20%;bottom:22%;background:repeating-linear-gradient(180deg,#353e42 0,#353e42 5px,rgba(0,0,0,0) 5px,rgba(0,0,0,0) 17px);-webkit-mask-image:linear-gradient(90deg,#000 62%,rgba(0,0,0,.35) 88%,transparent);mask-image:linear-gradient(90deg,#000 62%,rgba(0,0,0,.35) 88%,transparent)}
.cpy-stamp{right:11%;bottom:8%;width:22%;aspect-ratio:1;border-radius:50%;border:3px solid #3a4549}
.cpy-bar{left:0;right:0;top:0;height:100%;background:linear-gradient(180deg,rgba(120,220,200,0) 36%,rgba(120,220,200,.10) 44%,rgba(170,245,225,.45) 49%,var(--cpy-light) 49.7%,#ffffff 50%,var(--cpy-light) 50.3%,rgba(170,245,225,.45) 51%,rgba(120,220,200,.12) 56%,rgba(120,220,200,0) 64%);mix-blend-mode:screen;animation:cpy-scan var(--cpy-speed) cubic-bezier(.45,0,.55,1) infinite alternate;animation-delay:calc(var(--cpy-speed) * -.4);transform:translateY(12%)}
.cpy-dust{inset:0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .5 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");opacity:.06;mix-blend-mode:screen}
.cpy-sheen{inset:0;background:linear-gradient(115deg,rgba(255,255,255,0) 30%,rgba(255,255,255,.035) 42%,rgba(255,255,255,0) 52%),linear-gradient(90deg,rgba(18,22,24,.9) 0,rgba(18,22,24,.55) 40%,rgba(18,22,24,0) 60%)}
.cpy-eyebrow{color:#8fe8cf}
.cpy-copy{color:#c2cdcb}
.cpy-btn{background:#cdf7ea;color:#0f1716}
@keyframes cpy-scan{from{transform:translateY(-48%)}to{transform:translateY(48%)}}
@media (max-width:700px){.cpy-page{width:78%;right:-14%;top:auto;bottom:-24%;transform:rotate(6deg);opacity:.75}.cpy-sheen{background:linear-gradient(180deg,rgba(18,22,24,.75) 0,rgba(18,22,24,.5) 60%,rgba(18,22,24,.2))}}
</style>
<script>
(function () {
// Pauses the animation while the block is off screen, and stops it for reduced motion.
var roots = document.querySelectorAll(".cpy");
var still = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
roots.forEach(function (root) {
if (still) { root.setAttribute("data-reduced", ""); return; }
if (!("IntersectionObserver" in window)) return;
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) root.removeAttribute("data-paused");
else root.setAttribute("data-paused", "");
}).observe(root);
});
})();
</script>
With reduced motion: The light bar stops just below the middle of the sheet, leaving a lit stripe across the document.
Running cost: A single full-height layer moves with transform and blends with screen; everything else is static paint.
Pulses spread from a single point across a dark green chart of range circles and fine grid lines, and three contacts flare softly as the rings pass them. The chart is static gradients and each pulse is one scaling circle. It suits survey, maritime, security or monitoring services that want quiet, measured motion.
<section class="snr-root">
<div class="snr-art" aria-hidden="true">
<div class="snr-chart"></div>
<div class="snr-ring"></div>
<div class="snr-ring"></div>
<div class="snr-ring"></div>
<div class="snr-core"></div>
<div class="snr-blip snr-b1"></div>
<div class="snr-blip snr-b2"></div>
<div class="snr-blip snr-b3"></div>
<div class="snr-veil"></div>
</div>
<div class="snr-hero">
<p class="snr-eyebrow">Seabed survey crews</p>
<h1 class="snr-title">We map what lies under the hull.</h1>
<p class="snr-copy">Multibeam surveys for marinas, wind farm routes and salvage teams, with charts delivered within the week.</p>
<a class="snr-btn" href="#">Book a survey</a>
</div>
</section>
<style>
.snr-root{
/* Change these: */
--snr-ink: 150,226,190; /* ring colour as r,g,b */
--snr-period: 6s; /* time for one ring to reach the edge */
--snr-x: 70%; /* source position across */
--snr-y: 56%; /* source position down */
}
.snr-root{position:relative;display:flex;align-items:center;min-height:640px;isolation:isolate;overflow:hidden;font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
background:radial-gradient(90% 90% at var(--snr-x) var(--snr-y),#123227 0%,#0b1f19 45%,#06110d 100%)}
.snr-art{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.snr-chart{position:absolute;inset:0;
background:repeating-radial-gradient(circle at var(--snr-x) var(--snr-y),transparent 0 119px,rgba(var(--snr-ink),.10) 119px 120px),
linear-gradient(90deg,transparent calc(var(--snr-x) - .5px),rgba(var(--snr-ink),.10) calc(var(--snr-x) - .5px) calc(var(--snr-x) + .5px),transparent calc(var(--snr-x) + .5px)),
linear-gradient(180deg,transparent calc(var(--snr-y) - .5px),rgba(var(--snr-ink),.10) calc(var(--snr-y) - .5px) calc(var(--snr-y) + .5px),transparent calc(var(--snr-y) + .5px)),
linear-gradient(rgba(var(--snr-ink),.035) 1px,transparent 1px) 0 0/40px 40px,linear-gradient(90deg,rgba(var(--snr-ink),.035) 1px,transparent 1px) 0 0/40px 40px}
.snr-ring{position:absolute;left:var(--snr-x);top:var(--snr-y);width:2000px;height:2000px;margin:-1000px 0 0 -1000px;border-radius:50%;opacity:0;will-change:transform,opacity;
background:radial-gradient(closest-side,transparent 86%,rgba(var(--snr-ink),.10) 95%,rgba(var(--snr-ink),.65) 99.2%,transparent 100%);
animation:snr-ring var(--snr-period) cubic-bezier(.2,.5,.45,1) infinite}
.snr-ring:nth-child(3){animation-delay:calc(var(--snr-period) / -3)}
.snr-ring:nth-child(4){animation-delay:calc(var(--snr-period) * -2 / 3)}
.snr-core{position:absolute;left:var(--snr-x);top:var(--snr-y);width:10px;height:10px;margin:-5px 0 0 -5px;border-radius:50%;background:rgb(var(--snr-ink));box-shadow:0 0 0 6px rgba(var(--snr-ink),.12),0 0 30px 8px rgba(var(--snr-ink),.35)}
.snr-blip{position:absolute;width:8px;height:8px;margin:-4px 0 0 -4px;border-radius:50%;background:rgb(var(--snr-ink));box-shadow:0 0 14px 4px rgba(var(--snr-ink),.5);opacity:.15;animation:snr-blip calc(var(--snr-period) / 3) ease-out infinite}
.snr-b1{left:calc(var(--snr-x) - 200px);top:calc(var(--snr-y) - 120px);animation-delay:-.2s}
.snr-b2{left:calc(var(--snr-x) + 150px);top:calc(var(--snr-y) + 170px);animation-delay:-1.1s}
.snr-b3{left:calc(var(--snr-x) + 60px);top:calc(var(--snr-y) - 300px);animation-delay:-.6s}
.snr-veil{position:absolute;inset:0;background:linear-gradient(90deg,rgba(5,15,12,.75) 0%,rgba(5,15,12,.35) 45%,transparent 70%)}
@keyframes snr-ring{0%{transform:scale(.01);opacity:0}6%{opacity:1}75%{opacity:.5}100%{transform:scale(1);opacity:0}}
@keyframes snr-blip{0%{opacity:1;transform:scale(1.3)}60%{opacity:.25;transform:scale(1)}100%{opacity:.15;transform:scale(1)}}
@media (max-width:640px){.snr-root{--snr-x:76%;--snr-y:24%}}
.snr-hero{position:relative;z-index:1;box-sizing:border-box;width:100%;max-width:1120px;margin:0 auto;padding:112px 20px;text-align:left}
.snr-hero > *{max-width:560px}
.snr-eyebrow{margin:0 0 18px;font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#9fe3c3}
.snr-title{margin:0;font-size:clamp(2.1rem,5.4vw,3.6rem);line-height:1.06;letter-spacing:-.025em;font-weight:650;color:#eefaf4;text-wrap:balance}
.snr-copy{margin:20px 0 0;max-width:34em;font-size:clamp(1rem,1.6vw,1.125rem);line-height:1.6;color:#c4d9cf}
.snr-btn{display:inline-flex;align-items:center;justify-content:center;min-height:46px;margin-top:32px;padding:0 24px;border-radius:8px;background:#9fe3c3;color:#06150f;font-size:15px;font-weight:600;text-decoration:none;box-shadow:none;transition:transform .2s ease,background-color .2s ease}
.snr-btn:hover{transform:translateY(-1px);background:#b8edd4}
.snr-btn:focus-visible{outline:2px solid #c9f3df;outline-offset:3px}
@media (max-width:640px){.snr-hero{padding:88px 20px}}
.snr-root[data-paused] *,.snr-root[data-paused] *::before,.snr-root[data-paused] *::after{animation-play-state:paused!important}
@media (prefers-reduced-motion: reduce){
.snr-ring{opacity:.5;transform:scale(.3)}.snr-ring:nth-child(3){opacity:.3;transform:scale(.62)}.snr-ring:nth-child(4){opacity:0}.snr-blip{opacity:.7}
.snr-root *,.snr-root *::before,.snr-root *::after{animation:none!important;transition:none!important}
}
</style>
<script>
(function () {
// Pauses the animation while the section is off screen. Not needed for reduced motion.
if (!("IntersectionObserver" in window)) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
var roots = document.querySelectorAll(".snr-root");
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) entry.target.removeAttribute("data-paused");
else entry.target.setAttribute("data-paused", "");
});
});
roots.forEach(function (el) { io.observe(el); });
})();
</script>
With reduced motion: Two rings hold still part way out and the three contacts stay softly lit.
Running cost: Three scaling rings and three blinking dots, all transform and opacity; the chart is painted once.
A flat poster block in clay, ochre and deep brown with chunky two-pixel specks, the kind of texture a printed screen poster has. A short script paints one grain tile on a canvas when the block loads, then CSS jumps it around on a fast stepped loop. If canvas is blocked, a finer SVG noise tile takes its place, so the poster never looks bare.
<section class="w4ter">
<div class="w4ter-art" aria-hidden="true">
<div class="w4ter-sun"></div>
<div class="w4ter-arch"></div>
<div class="w4ter-fade"></div>
</div>
<div class="w4ter-inner">
<span class="w4ter-tag">Kiln Lane Pottery</span>
<h2 class="w4ter-title">Open studio weekend</h2>
<p class="w4ter-copy">Walk round the wheels and the kiln room, watch a firing being unpacked, and take home a seconds bowl straight off the shelf.</p>
<ul class="w4ter-facts">
<li><span>When</span>Sat 8 and Sun 9 November</li>
<li><span>Hours</span>10:00 to 16:00</li>
<li><span>Where</span>22 Tanner Lane, unit 4</li>
</ul>
<a href="#" class="w4ter-cta">Plan your visit</a>
</div>
</section>
<style>
/* Change these: --w4ter-clay (poster colour), --w4ter-cream (text), --w4ter-grain (grain strength), and .5s on .w4ter-art::after (grain speed). */
.w4ter { --w4ter-clay: #9f4528; --w4ter-cream: #f3e4cc; --w4ter-dark: #3a1b10; --w4ter-grain: .42;
position: relative; isolation: isolate; overflow: hidden; background: var(--w4ter-clay); color: var(--w4ter-cream); font-family: ui-sans-serif, system-ui, sans-serif; }
.w4ter-art { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.w4ter-sun { position: absolute; right: 8%; top: 12%; width: min(46vw, 420px); aspect-ratio: 1; border-radius: 50%; background: #e7a34d; }
.w4ter-arch { position: absolute; right: 2%; bottom: -2px; width: min(58vw, 540px); height: 72%; border-radius: 999px 999px 0 0; background: #7c3119; }
.w4ter-arch::after { content: ""; position: absolute; left: 22%; right: 22%; bottom: 0; height: 58%; border-radius: 999px 999px 0 0; background: var(--w4ter-dark); }
.w4ter-fade { position: absolute; inset: 0; background: linear-gradient(90deg, var(--w4ter-clay) 0%, rgba(159, 69, 40, .92) 34%, rgba(159, 69, 40, 0) 62%); }
/* grain: an SVG noise tile by default, swapped for a coarser canvas tile by the script */
.w4ter-art::after { content: ""; position: absolute; inset: -140px; opacity: var(--w4ter-grain);
background-image: var(--w4ter-tile, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='t'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .2 0 0 0 0 .08 0 0 0 0 .03 0 0 0 2 -1'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23t)'/%3E%3C/svg%3E"));
animation: w4ter-grain .5s steps(1) infinite; }
.w4ter[data-w4ter-off] .w4ter-art::after { animation-play-state: paused; }
@keyframes w4ter-grain { 0% { transform: translate(0, 0); } 20% { transform: translate(-53px, 41px); } 40% { transform: translate(37px, -67px); } 60% { transform: translate(-88px, -19px); } 80% { transform: translate(74px, 58px); } }
.w4ter-inner { position: relative; max-width: 68rem; margin: 0 auto; padding: 72px 24px 80px; }
.w4ter-tag { display: inline-block; padding: 7px 12px; border: 1.5px solid var(--w4ter-cream); border-radius: 999px; font-size: 12px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; }
.w4ter-title { margin: 26px 0 0; max-width: 11ch; font-size: clamp(48px, 9vw, 112px); font-weight: 800; line-height: .9; letter-spacing: -.035em; text-transform: uppercase; }
.w4ter-copy { margin: 22px 0 0; max-width: 26rem; font-size: 17px; line-height: 1.6; color: #fbeedd; }
.w4ter-facts { margin: 28px 0 0; padding: 0; list-style: none; display: grid; gap: 4px; font-size: 15px; font-weight: 600; }
.w4ter-facts span { display: inline-block; min-width: 5.5em; font-weight: 500; color: #f7dcc0; }
.w4ter-cta { display: inline-flex; align-items: center; min-height: 48px; margin-top: 30px; padding: 0 24px; border-radius: 999px; background: var(--w4ter-cream);
color: var(--w4ter-dark); font-size: 15px; font-weight: 700; text-decoration: none; }
.w4ter-cta:hover { background: #fff6ea; }
.w4ter-cta:focus-visible { outline: 3px solid var(--w4ter-cream); outline-offset: 3px; }
@media (max-width: 640px) { .w4ter-sun { right: -18%; top: 4%; opacity: .55; } .w4ter-arch { right: -30%; opacity: .5; } }
@media (prefers-reduced-motion: reduce) { .w4ter-art::after { animation: none !important; } }
</style>
<script>
(function () {
/* Change these: TILE size in px, SPECKS share of pixels with a speck, CLUMP speck size in px. */
var TILE = 180, SPECKS = 0.22, CLUMP = 2;
var root = document.querySelector(".w4ter");
if (!root) return;
/* draws one tile of warm specks once; if canvas fails the SVG grain in the CSS stays */
var c = document.createElement("canvas"), g = c.getContext && c.getContext("2d");
if (g) {
c.width = c.height = TILE;
for (var y = 0; y < TILE; y += CLUMP) for (var x = 0; x < TILE; x += CLUMP) {
if (Math.random() > SPECKS) continue;
g.fillStyle = Math.random() < 0.6 ? "rgba(58,27,16," + (0.35 + Math.random() * 0.5).toFixed(2) + ")" : "rgba(255,240,220," + (0.2 + Math.random() * 0.4).toFixed(2) + ")";
g.fillRect(x, y, CLUMP, CLUMP);
}
try { root.style.setProperty("--w4ter-tile", "url(" + c.toDataURL("image/png") + ")"); } catch (e) {}
}
/* pauses the grain while the poster is off screen */
new IntersectionObserver(function (en) { root.toggleAttribute("data-w4ter-off", !en[0].isIntersecting); }).observe(root);
})();
</script>
With reduced motion: The clay colour, sun, arch and coarse specks all stay, frozen in one position.
Running cost: The canvas draws one small tile a single time on load; after that only a CSS loop jumps that tile between five offsets, paused off screen.
Browsers can move and fade a layer on the graphics chip without repainting it, so these effects animate transform and opacity and leave background-position, width and blur values alone. That keeps the main thread free for your own scripts and keeps scrolling smooth on mid-range phones.
A large blurred layer is the costly part of most CSS backgrounds. Where an effect needs softness it uses a pre-blurred gradient instead of a live filter on a moving element. If you stack several effects on one page, keep only the one in view running: the included observer sets animation-play-state to paused when the block leaves the screen.
Yes. Each effect here runs on @keyframes alone. The short script only pauses the animation when the section is scrolled out of view, and you can delete it if the page is short.
Transform and opacity are the cheap ones because the browser can composite them without layout or paint. Animating background-position, box-shadow or filter forces repaints on every frame and is what makes many background effects stutter.
Make the section position: relative, give the background layer position: absolute with inset: 0 and a lower z-index, then place your content in a sibling with position: relative. Check the text contrast against the brightest frame of the animation, not the first one.
A prefers-reduced-motion media query in each snippet stops the keyframes and shows one settled frame, so the section still has its colour and texture without movement.
More collections: Animated Gradient Backgrounds, CSS Wave Animations, Animated Backgrounds for Websites. All animated backgrounds.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.