Flow

Effects / CSS

Holographic foil

A card with a rainbow sheen that shifts as the pointer moves, like a rare trading card.

Prompt to build it

Build a holographic foil card that tilts toward the pointer with a shifting rainbow sheen. Use CSS variables updated on pointermove for tilt and gradient position, a repeating linear rainbow with mix-blend-mode: color-dodge, and a glare highlight. Make it responsive, cap devicePixelRatio at 2, pause when off-screen, and show a still frame when prefers-reduced-motion is set. No external libraries.

Tested code

One self-contained HTML file with no libraries: the same code that runs above. Your AI gets it from Flow's get_effect tool.

Show the code
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Holographic foil | Flow effect</title> <!-- Flow effect: Holographic foil (https://flow.donta.io/effects/holographic-foil) Self-contained: no libraries. Copy the .fx-stage element, the CSS and the script into your project and size .fx-stage however you like. --> <style> :root { --display: "Bricolage Grotesque", ui-sans-serif, system-ui, sans-serif; --body: ui-sans-serif, system-ui, sans-serif; } html, body { margin: 0; min-height: 100%; background: #0e0e10; display: grid; place-items: center; } .fx-stage { width: min(100vw, 960px); } /* Stage that every effect fills. Container units (cqw/cqmin) size the art. */ .fx-stage { position: relative; aspect-ratio: 4 / 3; overflow: hidden; border-radius: 16px; container-type: size; background: #111; isolation: isolate; } .fx-fill { position: absolute; inset: 0; } .fx-fallback { position: absolute; inset: 0; } .fx-center { display: grid; place-items: center; } /* ── Holographic foil ─────────────────────────── */ @property --hx { syntax: "<number>"; inherits: true; initial-value: 0.5; } @property --hy { syntax: "<number>"; inherits: true; initial-value: 0.5; } .fx-holo { background: #0d0d12; perspective: 800px; } .fx-holo-card { --hx: 0.5; --hy: 0.5; position: relative; width: 44cqmin; aspect-ratio: 5 / 7; border-radius: 4cqmin; background: #1a1a22; overflow: hidden; transform: rotateY(calc((var(--hx) - 0.5) * 24deg)) rotateX(calc((0.5 - var(--hy)) * 24deg)); animation: fx-holo-drift 6s ease-in-out infinite; display: grid; place-items: center; box-shadow: 0 4cqmin 10cqmin rgba(0, 0, 0, 0.5); } .fx-holo-card.is-live { animation: none; } .fx-holo-card::before { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(115deg, #ff7a7a, #ffd36b 8%, #8affb0 16%, #6be0ff 24%, #a98bff 32%, #ff7a7a 40%); background-size: 300% 300%; background-position: calc(var(--hx) * 100%) calc(var(--hy) * 100%); mix-blend-mode: color-dodge; opacity: 0.9; } .fx-holo-card::after { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at calc(var(--hx) * 100%) calc(var(--hy) * 100%), rgba(255, 255, 255, 0.55), transparent 45%); mix-blend-mode: overlay; } .fx-holo-card b { position: relative; z-index: 1; font: 800 11cqmin/1 var(--display); letter-spacing: 0.04em; color: rgba(255, 255, 255, 0.85); mix-blend-mode: overlay; } @keyframes fx-holo-drift { 0%, 100% { --hx: 0.2; --hy: 0.35; } 50% { --hx: 0.8; --hy: 0.65; } } @media (prefers-reduced-motion: reduce) { .fx-stage *, .fx-stage *::before, .fx-stage *::after { animation-play-state: paused !important; } } </style> </head> <body> <div class="fx-stage"> <div class="fx-fill fx-center fx-holo"><div class="fx-holo-card"><b>HOLO</b></div></div> </div> <script> document.querySelectorAll(".fx-holo").forEach((stage) => { const card = stage.querySelector(".fx-holo-card"); stage.addEventListener("pointermove", (e) => { const r = card.getBoundingClientRect(); card.classList.add("is-live"); card.style.setProperty("--hx", String(Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)))); card.style.setProperty("--hy", String(Math.min(1, Math.max(0, (e.clientY - r.top) / r.height)))); }); stage.addEventListener("pointerleave", () => card.classList.remove("is-live")); }); </script> </body> </html>

How it works

Layer a conic or linear rainbow gradient with mix-blend-mode over the card, move its position with the pointer, and tilt the card slightly in 3D.

Guardrails

  • Limit tilt to about 12 degrees; disable tilt for reduced motion.
  • Respect prefers-reduced-motion: show a still frame instead of the animation.
  • Pause when off-screen or when the tab is hidden (IntersectionObserver + visibilitychange).
  • Keep text off busy animated areas, or give it a solid backing that passes WCAG contrast.
  • One showpiece effect per page. If everything moves, nothing stands out.