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>