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

Holographic CSS Effect: Iridescent Rainbow Shimmer on Any Element

Build iridescent holographic shimmer with pure CSS — conic gradients, hue rotation, and mouse-tracking tilt for cards, buttons, and badges.

iridescent holographic rainbow shimmer effect on dark card surface

What the Holographic Effect Actually Is

A holographic CSS effect mimics the iridescent rainbow you see on trading cards, foil stickers, and those early-2000s chrome credit cards. As light (or in our case, a mouse cursor) moves across the surface, the color shifts continuously — magenta bleeds into cyan, gold into green, then back again. It's not just a gradient. It's a gradient that *moves* based on position.

The core mechanic is simple once you break it down. You're stacking a conic-gradient or linear-gradient with background-position animating via CSS custom properties that JavaScript updates on mousemove. Pair that with hue-rotate on a ::after pseudo-element and you get the full spectrum cycling effect without a single image asset.

Worth noting: this effect became dramatically easier after Chrome 112 shipped support for @property with <angle> syntax in 2023. Before that, animating hue-rotate smoothly meant manual JS ticking. Now you can declare a registered custom property and CSS transitions handle interpolation natively.

Honestly, half the holographic CSS tutorials out there are overcomplicating it. You don't need WebGL, canvas, or a shader library. A few layered gradients and a 20-line JS snippet gets you 90% of the way there.

The Base CSS: Conic Gradient + Hue Rotation

Start with the gradient layer itself. A conic-gradient that cycles through the full hue wheel is your foundation — you want it to repeat, which means setting background-size to something like 200% 200% so there's room to shift it around without showing edges.

.holo-card {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  background: #111;
  isolation: isolate;
}

.holo-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: conic-gradient(
    from var(--holo-angle, 0deg) at 50% 50%,
    hsl(0 80% 60%),
    hsl(60 80% 60%),
    hsl(120 80% 60%),
    hsl(180 80% 60%),
    hsl(240 80% 60%),
    hsl(300 80% 60%),
    hsl(360 80% 60%)
  );
  opacity: 0;
  transition: opacity 0.3s ease;
  mix-blend-mode: color-dodge;
  z-index: 1;
}

.holo-card:hover::before {
  opacity: 0.6;
}

The mix-blend-mode: color-dodge is doing a lot of heavy lifting here. Instead of the gradient just painting over your card content, it brightens and shifts the underlying colors, which is exactly how real foil reacts to ambient light. Swap it for screen if you want something subtler — screen is more forgiving on light backgrounds.

That said, color-dodge blows out easily. Keep that opacity value between 0.4 and 0.7. Anything above 0.8 and you'll lose contrast on text and icons sitting on top of the effect.

One more thing — isolation: isolate on the parent is non-negotiable. Without it, mix-blend-mode bleeds into parent elements and you'll spend 45 minutes wondering why your navbar is turning purple.

Adding Mouse-Tracking Tilt with JavaScript

Static holographic shimmer looks cool for about two seconds. The thing that makes it feel genuinely premium is tying the gradient angle and a CSS 3D tilt to mouse position. When the cursor moves across the card, the holo layer rotates and the card tips toward the pointer — exactly like a physical foil card.

const cards = document.querySelectorAll('.holo-card');

cards.forEach(card => {
  card.addEventListener('mousemove', (e) => {
    const rect = card.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width;  // 0 to 1
    const y = (e.clientY - rect.top) / rect.height;

    const rotateX = (y - 0.5) * -20; // degrees
    const rotateY = (x - 0.5) * 20;
    const angle = Math.atan2(y - 0.5, x - 0.5) * (180 / Math.PI);

    card.style.setProperty('--holo-angle', `${angle}deg`);
    card.style.setProperty('--rotate-x', `${rotateX}deg`);
    card.style.setProperty('--rotate-y', `${rotateY}deg`);
  });

  card.addEventListener('mouseleave', () => {
    card.style.setProperty('--holo-angle', '0deg');
    card.style.setProperty('--rotate-x', '0deg');
    card.style.setProperty('--rotate-y', '0deg');
  });
});

Then wire those custom properties into the card's transform on the CSS side. Add transform: perspective(800px) rotateX(var(--rotate-x, 0deg)) rotateY(var(--rotate-y, 0deg)) to .holo-card and you've got 3D tilt for free. Keep the perspective at 600px1000px — below 400px it gets too dramatic and above 1200px you barely notice it.

Quick aside: if you're building this in React, you'd attach these as onMouseMove and onMouseLeave props and store the values in a useRef rather than a useState — you don't want re-renders firing on every pixel of cursor movement.

// React version — useRef keeps it off the render cycle
const cardRef = useRef<HTMLDivElement>(null);

const handleMouseMove = useCallback((e: React.MouseEvent) => {
  const el = cardRef.current;
  if (!el) return;
  const rect = el.getBoundingClientRect();
  const x = (e.clientX - rect.left) / rect.width;
  const y = (e.clientY - rect.top) / rect.height;
  const angle = Math.atan2(y - 0.5, x - 0.5) * (180 / Math.PI);
  el.style.setProperty('--holo-angle', `${angle}deg`);
  el.style.setProperty('--rotate-x', `${(y - 0.5) * -20}deg`);
  el.style.setProperty('--rotate-y', `${(x - 0.5) * 20}deg`);
}, []);

The Shimmer Sweep Layer

The tilt-and-rotate combo handles the directional color shift, but real holographic foil also has a white shimmer sweep — a highlight that races across the surface as the angle changes. You add this with a second pseudo-element: ::after, running a linear-gradient from transparent white to transparent white with a bright white band in the middle.

.holo-card::after {
  content: '';
  position: absolute;
  inset: -50%;
  background: linear-gradient(
    105deg,
    transparent 40%,
    rgba(255, 255, 255, 0.4) 50%,
    transparent 60%
  );
  transform: translateX(calc(var(--rotate-y, 0deg) * 3px));
  transition: transform 0.1s ease-out;
  mix-blend-mode: overlay;
  pointer-events: none;
  z-index: 2;
}

The inset: -50% trick is there so the gradient has room to slide out of frame — if you constrain it to inset: 0 the shimmer band disappears at the edges instead of gliding off cleanly. It's a little wasteful in terms of paint area, but the GPU handles this without breaking a sweat on any device from 2021 onward.

In practice, mix-blend-mode: overlay on the shimmer sweep plays nicer with dark backgrounds than screen does. On a #0a0a0a card with overlay, the white band adds luminosity without washing out the holo gradient underneath it.

Look, if you want this effect on a button rather than a full card, the same CSS applies — just drop the 3D tilt transform and keep the gradient rotation. A 40px-tall button with a sweeping holo shimmer on hover is genuinely satisfying and takes about 10 lines of CSS total.

CSS-Only Animated Version (No JavaScript)

Mouse-tracking is great, but sometimes you need a version that runs on its own — for badges, loading states, or elements that users might not hover. The purely CSS approach uses @keyframes with hue-rotate and background-position animation. Less interactive, but works everywhere including mobile.

@property --holo-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

@keyframes holo-spin {
  0%   { --holo-angle: 0deg; }
  100% { --holo-angle: 360deg; }
}

.holo-badge {
  position: relative;
  border-radius: 999px;
  padding: 6px 16px;
  background: #111;
  color: #fff;
  isolation: isolate;
  overflow: hidden;
}

.holo-badge::before {
  content: '';
  position: absolute;
  inset: 0;
  background: conic-gradient(
    from var(--holo-angle) at 50% 50%,
    hsl(0 90% 65%),
    hsl(90 90% 65%),
    hsl(180 90% 65%),
    hsl(270 90% 65%),
    hsl(360 90% 65%)
  );
  opacity: 0.5;
  mix-blend-mode: color-dodge;
  animation: holo-spin 3s linear infinite;
}

The @property declaration is what makes this tick — without it, CSS can't interpolate between angle values in a keyframe animation and you'd get a jump cut on loop instead of a smooth spin. This syntax has been in Chromium since version 85, Firefox since 128, and Safari since 16.4, so browser support in 2026 is essentially universal.

Want it slower and more ambient? Push the animation duration to 6s or even 10s. Faster than 2s and it starts to look like a loading spinner, which probably isn't what you're going for.

Applying Holographic to Text

The card treatment is the obvious use case, but holographic text is arguably more striking. The technique uses background-clip: text with color: transparent — you've probably seen this for gradient text, and holographic is the same pattern with a conic-gradient source and animation on top.

.holo-text {
  background: conic-gradient(
    from var(--holo-angle, 0deg) at 50% 50%,
    hsl(0 100% 70%),
    hsl(60 100% 70%),
    hsl(120 100% 70%),
    hsl(180 100% 70%),
    hsl(240 100% 70%),
    hsl(300 100% 70%),
    hsl(360 100% 70%)
  );
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-weight: 800;
  font-size: clamp(2rem, 6vw, 5rem);
  animation: holo-spin 4s linear infinite;
}

One gotcha: background-clip: text clips to the rendered glyph outlines, so thin typefaces lose most of the color variation. Use a heavy weight — 700 or 800 minimum — so there's enough ink coverage to see the gradient cycling. At 800px letter width you'll see the full spectrum. At 200px on a light typeface you'll barely notice the holo.

This pairs really well with glassmorphism components — holographic heading text over a frosted glass panel is a current favourite in SaaS hero sections. Check out Empire UI's glassmorphism generator if you want to dial in the glass backdrop values to complement the text.

Performance, Accessibility, and Practical Limits

Before you put this on 40 cards on a single page — don't. mix-blend-mode triggers compositing and can cause paint thrashing if you're running many blended pseudo-elements simultaneously. Benchmark with Chrome DevTools' Performance panel. In testing, 4–6 holo cards with mouse tracking runs at a solid 60fps on integrated graphics. Beyond 10 and you start to see frame drops on mid-range hardware.

For accessibility, wrap the animation in prefers-reduced-motion. The tilt-on-hover is fine to keep since it requires active mouse interaction, but the CSS-only infinite spinning version should pause for users who've opted out of motion.

@media (prefers-reduced-motion: reduce) {
  .holo-badge::before {
    animation: none;
    --holo-angle: 45deg; /* static snapshot */
  }
  .holo-text {
    animation: none;
    background: linear-gradient(135deg, #a78bfa, #38bdf8);
  }
}

If you're combining this with other visual effects, be selective. Holographic shimmer already competes for attention — pair it with something structurally grounded, like a subtle box shadow or a quiet background rather than an animated mesh gradient. You can also explore how this technique overlaps with related styles like aurora backgrounds or vaporwave aesthetics, both of which share the iridescent color palette but express it differently. The gradient generator is useful for nailing the base colors if you want to customize the hue stops beyond the full-spectrum defaults shown here.

FAQ

Does the holographic CSS effect work without JavaScript?

Yes — use @keyframes with @property to animate --holo-angle directly in CSS. The mouse-tracking tilt needs JS, but the shimmer and color cycling are fully CSS-only.

Which blend mode looks best for holographic cards?

color-dodge gives the most authentic foil look on dark backgrounds. Use screen or overlay if your card has a lighter or mid-tone base color.

Will this hurt performance on mobile?

The CSS-only version is fine. Mouse-tracking JS doesn't fire on touchscreens, so mobile users just see the static or keyframe-animated version. Check paint with DevTools if you're stacking multiple blended layers.

Can I apply the holographic effect to a button or badge instead of a card?

Absolutely — drop the 3D tilt transform and keep just the gradient rotation and shimmer sweep. Works great on pill badges and CTA buttons with no extra work.

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

Read next

Holographic Effect in CSS: Rainbow Foil Cards and BadgesAnimated Gradient Border in CSS: Spinning Rainbow Border on Any ElementSkeleton Loading Animation in CSS: Shimmer, Pulse, Wave VariantsCSS Gradient Animation: background-size, background-position Tricks