EmpireUI
Get Pro
← Blog8 min read#holographic#css#gradient

Holographic Effect in CSS: Rainbow Foil Cards and Badges

Build rainbow foil holographic cards and badges in pure CSS — conic-gradient, hue-rotate animations, and mouse-tracking tilt that actually looks like trading card foil.

Rainbow holographic foil light refracting across a shimmering surface

What Makes a CSS Effect Actually Look Holographic

Holographic foil has two things that most CSS gradient tutorials completely miss: angular light shift and surface iridescence. You're not just slapping a rainbow gradient on a card. The colors change based on viewing angle, and that's the part you need to fake.

In practice, the illusion comes from two CSS layers working together — a conic-gradient or repeating-linear-gradient for the color shift, and a CSS animation that rotates the hue as the card moves. Nail both and you get something that reads as physical. Get only one and it just looks like a pride flag.

Worth noting: this technique has been floating around since roughly 2019 but really hit its stride when @property landed in Chrome 85, letting you animate custom properties natively. That unlocked smooth hue-rotation without the janky keyframe hacks people were doing before.

If you've already been playing with glassmorphism components or other translucent UI effects, holographic sits in a nearby design space — both rely on light-play — but holographic skips the blur and goes full spectral.

The Core CSS: Conic Gradient and Hue-Rotate

The base layer is a conic-gradient that sweeps the full color wheel. Think of it as the foil substrate. On top of that, you animate filter: hue-rotate() so the entire spectrum shifts over time. Simple. Effective.

Here's the minimal card that actually works: ``css .holo-card { position: relative; width: 320px; height: 480px; border-radius: 16px; overflow: hidden; background: #1a1a2e; box-shadow: 0 8px 32px rgba(0,0,0,0.4); } .holo-card::before { content: ''; position: absolute; inset: 0; background: conic-gradient( from 0deg, hsl(0, 100%, 65%), hsl(60, 100%, 65%), hsl(120, 100%, 65%), hsl(180, 100%, 65%), hsl(240, 100%, 65%), hsl(300, 100%, 65%), hsl(360, 100%, 65%) ); mix-blend-mode: color-dodge; opacity: 0.6; animation: holo-shift 4s linear infinite; } @keyframes holo-shift { 0% { filter: hue-rotate(0deg) brightness(1); } 50% { filter: hue-rotate(180deg) brightness(1.3); } 100% { filter: hue-rotate(360deg) brightness(1); } } ` The mix-blend-mode: color-dodge` is doing most of the heavy lifting here. It punches the gradient color into whatever's below it, which is why the dark card background still shows through while the foil shimmers on top.

Honestly, color-dodge alone is worth experimenting with for 30 minutes. It behaves very differently depending on whether your base is dark (near-black) or mid-tone. Dark backgrounds give you that deep foil look; lighter ones tend to wash out to near-white.

One more thing — the opacity: 0.6 on the pseudo-element is something you'll tune per use case. Badges want it higher (0.8+). Full cards look better around 0.5-0.65 so the content underneath stays readable.

Adding Mouse-Tracking Tilt for the Real Foil Feel

Static animation is fine. But the reason trading card collectors lose their minds isn't a looping animation — it's that the colors *react* to movement. You need mouse tracking.

Here's a React component that maps cursor position to CSS custom properties, then reads those in the stylesheet for perspective tilt and gradient shift: ``jsx import { useRef } from 'react'; export function HoloCard({ children }) { const cardRef = useRef(null); const handleMouseMove = (e) => { const card = cardRef.current; if (!card) return; const rect = card.getBoundingClientRect(); const x = (e.clientX - rect.left) / rect.width; // 0 to 1 const y = (e.clientY - rect.top) / rect.height; // 0 to 1 const rotateX = (y - 0.5) * -24; // ±12deg const rotateY = (x - 0.5) * 24; // ±12deg const bgX = x * 100; const bgY = y * 100; card.style.setProperty('--rotate-x', ${rotateX}deg); card.style.setProperty('--rotate-y', ${rotateY}deg); card.style.setProperty('--bg-x', ${bgX}%); card.style.setProperty('--bg-y', ${bgY}%); }; const handleMouseLeave = () => { const card = cardRef.current; if (!card) return; card.style.setProperty('--rotate-x', '0deg'); card.style.setProperty('--rotate-y', '0deg'); card.style.setProperty('--bg-x', '50%'); card.style.setProperty('--bg-y', '50%'); }; return ( <div ref={cardRef} className="holo-card" onMouseMove={handleMouseMove} onMouseLeave={handleMouseLeave} style={{ transform: 'perspective(800px) rotateX(var(--rotate-x, 0deg)) rotateY(var(--rotate-y, 0deg))', transition: 'transform 0.1s ease-out', }} > {children} </div> ); } ` Then in your CSS, update the ::before to use --bg-x and --bg-y` as the gradient center point so the foil peak follows the cursor.

The perspective(800px) value is a judgment call. 600px feels more dramatic and card-like. 1200px is subtle, almost imperceptible. 800px hits the sweet spot for a component that lives in a UI grid without screaming at the user.

Quick aside: on touch devices you'll want to attach onTouchMove and map touch.clientX/Y instead. The effect on mobile is genuinely impressive if you bother — most people don't.

Holographic Badges: Smaller Scale, Bigger Pop

Cards are easy because you have room. Badges are 32px tall and you've got 4 words of text. The challenge is making the foil readable, not just pretty.

For badges, drop the mix-blend-mode trick and go with a direct background on the badge itself with a background-size larger than the element. Then animate background-position. This gives you a sliding foil effect that works at any size: ``css .holo-badge { display: inline-flex; align-items: center; padding: 4px 12px; border-radius: 9999px; font-size: 12px; font-weight: 600; color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.5); background: linear-gradient( 135deg, hsl(0, 90%, 60%), hsl(60, 90%, 60%), hsl(120, 90%, 60%), hsl(180, 90%, 60%), hsl(240, 90%, 60%), hsl(300, 90%, 60%), hsl(0, 90%, 60%) ); background-size: 400% 100%; animation: badge-slide 3s linear infinite; } @keyframes badge-slide { 0% { background-position: 0% 50%; } 100% { background-position: 400% 50%; } } ` The background-size: 400% 100%` means the gradient is 4× the width of the badge, so you get a full spectrum sweep across the element before it loops.

If you want the badge to pair nicely with other UI elements, check what the gradient generator spits out for your specific color palette. Sometimes you don't want a full rainbow — a tighter 60° hue range looks more premium and less chaotic.

Look, the white text-shadow on the badge isn't optional. Without it, there will always be some point in the animation cycle where the text color matches the background and becomes unreadable. 1px blur, black or dark — don't skip it.

Performance: What to Watch Out For

Animating hue-rotate on a pseudo-element triggers compositing but not layout or paint — so it's generally GPU-accelerated. That said, if you've got 20 cards on screen simultaneously, you'll feel it on lower-end devices.

The will-change: transform, filter declaration on .holo-card::before is worth adding when you know the element will animate. It hints to the browser to promote the layer early. Don't spray it everywhere, but here it earns its keep.

Worth noting: mix-blend-mode forces the browser to composite that element against everything behind it. If your card sits over a complex background — gradient, image, other blended elements — you're paying extra. Test on a real mid-range Android, not just your MacBook Pro.

One more thing — the mouse-tracking tilt uses inline style updates on every mousemove. That's intentional and fine. CSS custom property updates via JS are cheap and don't cause style recalculation the same way direct property writes used to. But if you're seeing jank, throttle the handler to rAF.

Pairing Holographic With Other UI Styles

Holographic doesn't exist in isolation in a real UI. You'll combine it with something. And some combinations are much better than others.

It pairs extremely well with dark UI backgrounds — the darker the base, the more the foil pops. This is why you see it everywhere in gaming UIs and crypto NFT marketplaces. If you're working on a cyberpunk theme, holographic badges are basically mandatory.

It does not pair well with neumorphism. Neumorphism is all about subtle shadows and single-light-source realism. Holographic is iridescent spectral chaos. They fight each other. Pick one.

For product UIs that want a premium feel without going full gaming aesthetic, try limiting holographic to specific elements — a 'Pro' badge, a featured card border, a loading shimmer. You can also use the box shadow generator to add a matching colored glow that ties the holographic element into the surrounding design. Holographic as accent, not as wallpaper, is almost always the better call.

FAQ

Can I use holographic CSS effects without JavaScript?

Yes — the pure CSS version with @keyframes hue-rotate and conic-gradient needs zero JS. You only need JavaScript if you want mouse-tracking tilt, which is optional.

Why does mix-blend-mode: color-dodge look so washed out on my card?

Your base layer is probably too light. color-dodge burns toward white on mid-tone backgrounds. Use a dark background (#111 to #222 range) under the foil layer and it'll snap into the right look.

Does the tilt effect work on mobile touchscreens?

Not automatically — you need to add a touchmove handler that maps touch.clientX and touch.clientY the same way you'd handle mouse coordinates. It works well when you implement it.

How do I stop the holographic animation on reduced-motion preference?

Wrap your animation declarations in @media (prefers-reduced-motion: no-preference). Users with reduced-motion set will see the static gradient without any shifting or tilt.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Aurora Gradient CSS: Three Ways to Build the Effect From ScratchHolographic CSS Effect: Iridescent Rainbow Shimmer on Any ElementText Scramble Effect in CSS + JS: Glitch Characters on HoverCSS Gradient Animation: background-size, background-position Tricks