Glare Card — Free HTML CSS JS Holographic Tilt Snippet

Glare Card · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Pointer-driven 3D tilt
rotateX/rotateY follow the cursor with perspective.
Tracking glare hotspot
A radial light centered on --mx/--my.
Holographic foil
A panning conic gradient with color-dodge.
Realistic blend modes
soft-light and color-dodge react to the card.
Parallax content
translateZ floats the body above the surface.
Calm at rest
Effects fade in only on hover.
Single pointer handler
One listener drives tilt, glare, and foil.
Fully themeable
Colors, tilt, and depth are all tunable.

About this UI Snippet

Glare Card — Holographic Foil and Pointer-Tracking Glare

Screenshot of the Glare Card snippet rendered live

The glare card mimics a holographic trading card or foil credit card: it tilts in 3D toward your cursor while a bright glare hotspot and a rainbow foil sheen shift across its surface, exactly like catching the light on a real holo. This snippet builds it with plain HTML, CSS blend modes, and a single vanilla JavaScript pointer handler.

3D tilt from pointer position

The card lives in a perspective: 900px stage and uses transform-style: preserve-3d. A pointermove handler converts the cursor's position within the card into 0–1 fractions, then maps them to rotateX and rotateY values up to about 14° each — inverted on the vertical axis so the card tips toward the pointer rather than away. The short .12s transition smooths the motion without lagging behind the cursor. On pointerleave it resets to flat.

The glare hotspot

A .gl-glare layer is a radial gradient whose center is driven by two CSS custom properties, --mx and --my, updated from the same pointer handler. Because the gradient is centered at var(--mx) var(--my), the bright spot tracks exactly under the cursor. The layer uses mix-blend-mode: soft-light so the white glare brightens the card's colors realistically instead of painting an opaque white blob — it reads as light reflecting off a glossy surface.

The holographic foil

The signature rainbow comes from a .gl-foil layer filled with a conic-gradient cycling hot pink, purple, teal, and yellow, sized at 200% so it can pan. On pointer move, its background-position shifts with the cursor, making the colors flow and swirl. The key is mix-blend-mode: color-dodge, which makes the foil interact with the dark card beneath to produce the iridescent, metallic shimmer characteristic of holographic foil — a plain overlay would just look like a flat gradient. The foil fades in on hover so the card is calm at rest.

Depth on the content

The card body is pushed forward with translateZ(40px) inside the preserve-3d context, so as the card tilts, the text appears to float above the foil and glare layers with real parallax. This separation sells the 3D — the content and the surface effects move at visibly different depths.

Why blend modes matter here

Both surface effects rely on blend modes rather than opacity. soft-light and color-dodge compute their result from the colors underneath, so the same glare looks different over the dark navy card than it would over white — which is physically how reflections and foils behave. This is what separates a convincing holo effect from a generic gradient overlay.

Customizing it

Tune the 28 multiplier for a stronger or subtler tilt, change the glare radius and opacity, swap the foil's conic colors to theme the holo, or adjust the translateZ to push the content nearer or further. Replace the card content with a real product, ticket, or collectible. Pair it with a wobble card or a pin card for a set of tactile 3D cards, or use it as a premium pricing card tier.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA dark collectible card renders, calm at rest.
  2. 2
    Move the pointer over itThe card tilts in 3D toward your cursor.
  3. 3
    Watch the glareA bright hotspot follows the pointer like light on gloss.
  4. 4
    See the foilA rainbow holographic sheen flows as you move.
  5. 5
    Leave the cardIt eases back to flat.
  6. 6
    Theme the holoSwap the conic foil colors and tilt strength.

Real-world uses

Common Use Cases

NFT and collectibles
Show a holo card beside a pin card.
Premium pricing tiers
A flashy pricing card variant.
Membership cards
Pair with a wallet card flip.
Event tickets
Make a boarding pass feel special.
Product hero shots
Combine with a wobble card grid.
Blend-mode demos
A reference for color-dodge holographic foil.

Got questions?

Frequently Asked Questions

It sits in a perspective stage, and a pointermove handler converts the cursor's position inside the card to 0–1 fractions, then maps them to rotateX and rotateY up to about 14 degrees each. The vertical axis is inverted so the card tips toward the pointer. A short transition smooths it, and pointerleave resets to flat.

The foil is a panning conic-gradient layer set to mix-blend-mode: color-dodge. color-dodge computes its result from the dark card beneath, producing an iridescent metallic shimmer rather than a flat overlay. Its background-position shifts with the pointer so the rainbow flows as you move.

soft-light for the glare and color-dodge for the foil derive their colors from the layers underneath, so the same effect looks different over the dark card than it would over white — which is physically how reflections and foils behave. Plain opacity overlays look like flat gradients and break the holographic illusion.

The card uses transform-style: preserve-3d and the body is pushed forward with translateZ(40px). As the card tilts, the content moves at a different depth than the glare and foil layers, creating real parallax that separates the text from the surface and reinforces the 3D.

Render the structure as a component and keep the perspective and preserve-3d CSS. Handle pointermove and pointerleave with framework events, writing the transform and the --mx/--my/background-position via a ref so moves don't trigger re-renders. The blend-mode layers are pure CSS. In Tailwind, use perspective and transform utilities with mix-blend-color-dodge and mix-blend-soft-light.

Glare Card — Export as HTML, React, Vue, Angular & Tailwind

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Glare Card</title>
  <style>
    *{box-sizing:border-box;margin:0;padding:0}
    body{font-family:system-ui,-apple-system,sans-serif;background:#07070f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}
    
    .gl-stage{perspective:900px}
    .gl-card{position:relative;width:280px;height:380px;border-radius:20px;background:linear-gradient(160deg,#1b1b30,#0d0d1a);border:1px solid #2a2a45;overflow:hidden;transform-style:preserve-3d;transition:transform .12s ease-out;cursor:pointer;box-shadow:0 30px 60px -28px rgba(0,0,0,.8)}
    
    .gl-foil{position:absolute;inset:0;opacity:.0;background:conic-gradient(from 0deg,#ff0080,#7928ca,#2afadf,#ffea00,#ff0080);background-size:200% 200%;mix-blend-mode:color-dodge;transition:opacity .3s;pointer-events:none}
    .gl-card:hover .gl-foil{opacity:.35}
    
    .gl-glare{position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,.55),transparent 45%);opacity:0;transition:opacity .25s;mix-blend-mode:soft-light}
    .gl-card:hover .gl-glare{opacity:1}
    
    .gl-body{position:relative;z-index:1;height:100%;display:flex;flex-direction:column;padding:24px;transform:translateZ(40px)}
    .gl-chip{align-self:flex-start;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;background:rgba(122,40,202,.25);border:1px solid rgba(122,40,202,.5);color:#d8b4fe;padding:5px 11px;border-radius:999px}
    .gl-body h3{margin-top:auto;font-size:24px;font-weight:900;letter-spacing:-.02em}
    .gl-body p{font-size:13px;color:#a9a9c5;line-height:1.55;margin-top:8px}
    .gl-foot{display:flex;justify-content:space-between;margin-top:18px;font-size:12px;font-weight:700;color:#c7c7dd}
  </style>
</head>
<body>
  <div class="gl-stage">
    <article class="gl-card" id="glCard">
      <div class="gl-foil" aria-hidden="true"></div>
      <div class="gl-glare" aria-hidden="true"></div>
      <div class="gl-body">
        <span class="gl-chip">Holographic</span>
        <h3>Founders Edition</h3>
        <p>Tilt me. A foil sheen and a moving glare track your pointer in real time.</p>
        <div class="gl-foot"><span>#0042</span><span>★ Rare</span></div>
      </div>
    </article>
  </div>
  <script>
    var card = document.getElementById('glCard');
    var foil = card.querySelector('.gl-foil');
    
    card.addEventListener('pointermove', function (e) {
      var r = card.getBoundingClientRect();
      var px = (e.clientX - r.left) / r.width;    // 0..1
      var py = (e.clientY - r.top) / r.height;
    
      // 3D tilt toward the cursor (max ~14deg each axis).
      var rx = (0.5 - py) * 28;
      var ry = (px - 0.5) * 28;
      card.style.transform = 'rotateX(' + rx.toFixed(2) + 'deg) rotateY(' + ry.toFixed(2) + 'deg)';
    
      // Glare hotspot follows the pointer.
      card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
      card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
    
      // Shift the foil gradient so the holographic colors flow as you move.
      foil.style.backgroundPosition = (px * 100).toFixed(1) + '% ' + (py * 100).toFixed(1) + '%';
    });
    
    card.addEventListener('pointerleave', function () {
      card.style.transform = 'rotateX(0) rotateY(0)';
    });
  </script>
</body>
</html>
Tailwind
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Glare Card</title>
  <!-- Tailwind CSS v3+ — arbitrary value classes (e.g. bg-[#0f172a]) are valid -->
  <script src="https://cdn.tailwindcss.com"></script>
  <style>
    * {
      box-sizing:border-box;margin:0;padding:0
    }

    body {
      font-family:system-ui,-apple-system,sans-serif;background:#07070f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px
    }

    .gl-card:hover .gl-foil {
      opacity:.35
    }

    .gl-card:hover .gl-glare {
      opacity:1
    }

    .gl-body h3 {
      margin-top:auto;font-size:24px;font-weight:900;letter-spacing:-.02em
    }

    .gl-body p {
      font-size:13px;color:#a9a9c5;line-height:1.55;margin-top:8px
    }
  </style>
</head>
<body>
  <div class="[perspective:900px]">
    <article class="gl-card relative w-[280px] h-[380px] rounded-[20px] [background:linear-gradient(160deg,#1b1b30,#0d0d1a)] border border-[#2a2a45] overflow-hidden [transform-style:preserve-3d] [transition:transform_.12s_ease-out] cursor-pointer shadow-[0_30px_60px_-28px_rgba(0,0,0,.8)]" id="glCard">
      <div class="gl-foil absolute inset-0 opacity-0 [background:conic-gradient(from_0deg,#ff0080,#7928ca,#2afadf,#ffea00,#ff0080)] bg-[size:200%_200%] [mix-blend-mode:color-dodge] [transition:opacity_.3s] pointer-events-none" aria-hidden="true"></div>
      <div class="gl-glare absolute inset-0 pointer-events-none [background:radial-gradient(circle_at_var(--mx,50%)_var(--my,50%),rgba(255,255,255,.55),transparent_45%)] opacity-0 [transition:opacity_.25s] [mix-blend-mode:soft-light]" aria-hidden="true"></div>
      <div class="gl-body relative z-[1] h-full flex flex-col p-6 [transform:translateZ(40px)]">
        <span class="self-start text-[11px] font-bold tracking-[.05em] uppercase bg-[rgba(122,40,202,.25)] border border-[rgba(122,40,202,.5)] text-[#d8b4fe] py-[5px] px-[11px] rounded-[999px]">Holographic</span>
        <h3>Founders Edition</h3>
        <p>Tilt me. A foil sheen and a moving glare track your pointer in real time.</p>
        <div class="flex justify-between mt-[18px] text-xs font-bold text-[#c7c7dd]"><span>#0042</span><span>★ Rare</span></div>
      </div>
    </article>
  </div>
  <script>
    var card = document.getElementById('glCard');
    var foil = card.querySelector('.gl-foil');
    
    card.addEventListener('pointermove', function (e) {
      var r = card.getBoundingClientRect();
      var px = (e.clientX - r.left) / r.width;    // 0..1
      var py = (e.clientY - r.top) / r.height;
    
      // 3D tilt toward the cursor (max ~14deg each axis).
      var rx = (0.5 - py) * 28;
      var ry = (px - 0.5) * 28;
      card.style.transform = 'rotateX(' + rx.toFixed(2) + 'deg) rotateY(' + ry.toFixed(2) + 'deg)';
    
      // Glare hotspot follows the pointer.
      card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
      card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
    
      // Shift the foil gradient so the holographic colors flow as you move.
      foil.style.backgroundPosition = (px * 100).toFixed(1) + '% ' + (py * 100).toFixed(1) + '%';
    });
    
    card.addEventListener('pointerleave', function () {
      card.style.transform = 'rotateX(0) rotateY(0)';
    });
  </script>
</body>
</html>
React
import React, { useEffect } from 'react';

// CSS — optionally move to GlareCard.module.css
const css = `
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#07070f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}

.gl-stage{perspective:900px}
.gl-card{position:relative;width:280px;height:380px;border-radius:20px;background:linear-gradient(160deg,#1b1b30,#0d0d1a);border:1px solid #2a2a45;overflow:hidden;transform-style:preserve-3d;transition:transform .12s ease-out;cursor:pointer;box-shadow:0 30px 60px -28px rgba(0,0,0,.8)}

.gl-foil{position:absolute;inset:0;opacity:.0;background:conic-gradient(from 0deg,#ff0080,#7928ca,#2afadf,#ffea00,#ff0080);background-size:200% 200%;mix-blend-mode:color-dodge;transition:opacity .3s;pointer-events:none}
.gl-card:hover .gl-foil{opacity:.35}

.gl-glare{position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,.55),transparent 45%);opacity:0;transition:opacity .25s;mix-blend-mode:soft-light}
.gl-card:hover .gl-glare{opacity:1}

.gl-body{position:relative;z-index:1;height:100%;display:flex;flex-direction:column;padding:24px;transform:translateZ(40px)}
.gl-chip{align-self:flex-start;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;background:rgba(122,40,202,.25);border:1px solid rgba(122,40,202,.5);color:#d8b4fe;padding:5px 11px;border-radius:999px}
.gl-body h3{margin-top:auto;font-size:24px;font-weight:900;letter-spacing:-.02em}
.gl-body p{font-size:13px;color:#a9a9c5;line-height:1.55;margin-top:8px}
.gl-foot{display:flex;justify-content:space-between;margin-top:18px;font-size:12px;font-weight:700;color:#c7c7dd}
`;

export default function GlareCard() {
  // Auto-generated escape hatch: the original snippet's vanilla JS runs once
  // after mount and queries the rendered DOM. For idiomatic React, lift this
  // into state + handlers.
  useEffect(() => {
    const _listeners = [];
    const _originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener, options) {
      _listeners.push({ target: this, type, listener, options });
      return _originalAddEventListener.call(this, type, listener, options);
    };
    var card = document.getElementById('glCard');
    var foil = card.querySelector('.gl-foil');
    
    card.addEventListener('pointermove', function (e) {
      var r = card.getBoundingClientRect();
      var px = (e.clientX - r.left) / r.width;    // 0..1
      var py = (e.clientY - r.top) / r.height;
    
      // 3D tilt toward the cursor (max ~14deg each axis).
      var rx = (0.5 - py) * 28;
      var ry = (px - 0.5) * 28;
      card.style.transform = 'rotateX(' + rx.toFixed(2) + 'deg) rotateY(' + ry.toFixed(2) + 'deg)';
    
      // Glare hotspot follows the pointer.
      card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
      card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
    
      // Shift the foil gradient so the holographic colors flow as you move.
      foil.style.backgroundPosition = (px * 100).toFixed(1) + '% ' + (py * 100).toFixed(1) + '%';
    });
    
    card.addEventListener('pointerleave', function () {
      card.style.transform = 'rotateX(0) rotateY(0)';
    });
    // Cleanup: restore addEventListener and remove all listeners
    return () => {
      EventTarget.prototype.addEventListener = _originalAddEventListener;
      for (const { target, type, listener, options } of _listeners) {
        target.removeEventListener(type, listener, options);
      }
    };
  }, []);

  return (
    <>
      <style>{css}</style>
      <div className="gl-stage">
        <article className="gl-card" id="glCard">
          <div className="gl-foil" aria-hidden="true"></div>
          <div className="gl-glare" aria-hidden="true"></div>
          <div className="gl-body">
            <span className="gl-chip">Holographic</span>
            <h3>Founders Edition</h3>
            <p>Tilt me. A foil sheen and a moving glare track your pointer in real time.</p>
            <div className="gl-foot"><span>#0042</span><span>★ Rare</span></div>
          </div>
        </article>
      </div>
    </>
  );
}
React + Tailwind
import React, { useEffect } from 'react';
// Requires Tailwind CSS v3+ — https://tailwindcss.com/docs/installation
// Arbitrary value classes (e.g. bg-[#0f172a]) are valid Tailwind v3+

export default function GlareCard() {
  // Auto-generated escape hatch: the original snippet's vanilla JS runs once
  // after mount and queries the rendered DOM. For idiomatic React, lift this
  // into state + handlers.
  useEffect(() => {
    const _listeners = [];
    const _originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener, options) {
      _listeners.push({ target: this, type, listener, options });
      return _originalAddEventListener.call(this, type, listener, options);
    };
    var card = document.getElementById('glCard');
    var foil = card.querySelector('.gl-foil');
    
    card.addEventListener('pointermove', function (e) {
      var r = card.getBoundingClientRect();
      var px = (e.clientX - r.left) / r.width;    // 0..1
      var py = (e.clientY - r.top) / r.height;
    
      // 3D tilt toward the cursor (max ~14deg each axis).
      var rx = (0.5 - py) * 28;
      var ry = (px - 0.5) * 28;
      card.style.transform = 'rotateX(' + rx.toFixed(2) + 'deg) rotateY(' + ry.toFixed(2) + 'deg)';
    
      // Glare hotspot follows the pointer.
      card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
      card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
    
      // Shift the foil gradient so the holographic colors flow as you move.
      foil.style.backgroundPosition = (px * 100).toFixed(1) + '% ' + (py * 100).toFixed(1) + '%';
    });
    
    card.addEventListener('pointerleave', function () {
      card.style.transform = 'rotateX(0) rotateY(0)';
    });
    // Cleanup: restore addEventListener and remove all listeners
    return () => {
      EventTarget.prototype.addEventListener = _originalAddEventListener;
      for (const { target, type, listener, options } of _listeners) {
        target.removeEventListener(type, listener, options);
      }
    };
  }, []);

  return (
    <>
      <style>{`
* {
  box-sizing:border-box;margin:0;padding:0
}

body {
  font-family:system-ui,-apple-system,sans-serif;background:#07070f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px
}

.gl-card:hover .gl-foil {
  opacity:.35
}

.gl-card:hover .gl-glare {
  opacity:1
}

.gl-body h3 {
  margin-top:auto;font-size:24px;font-weight:900;letter-spacing:-.02em
}

.gl-body p {
  font-size:13px;color:#a9a9c5;line-height:1.55;margin-top:8px
}
      `}</style>
      <div className="[perspective:900px]">
        <article className="gl-card relative w-[280px] h-[380px] rounded-[20px] [background:linear-gradient(160deg,#1b1b30,#0d0d1a)] border border-[#2a2a45] overflow-hidden [transform-style:preserve-3d] [transition:transform_.12s_ease-out] cursor-pointer shadow-[0_30px_60px_-28px_rgba(0,0,0,.8)]" id="glCard">
          <div className="gl-foil absolute inset-0 opacity-0 [background:conic-gradient(from_0deg,#ff0080,#7928ca,#2afadf,#ffea00,#ff0080)] bg-[size:200%_200%] [mix-blend-mode:color-dodge] [transition:opacity_.3s] pointer-events-none" aria-hidden="true"></div>
          <div className="gl-glare absolute inset-0 pointer-events-none [background:radial-gradient(circle_at_var(--mx,50%)_var(--my,50%),rgba(255,255,255,.55),transparent_45%)] opacity-0 [transition:opacity_.25s] [mix-blend-mode:soft-light]" aria-hidden="true"></div>
          <div className="gl-body relative z-[1] h-full flex flex-col p-6 [transform:translateZ(40px)]">
            <span className="self-start text-[11px] font-bold tracking-[.05em] uppercase bg-[rgba(122,40,202,.25)] border border-[rgba(122,40,202,.5)] text-[#d8b4fe] py-[5px] px-[11px] rounded-[999px]">Holographic</span>
            <h3>Founders Edition</h3>
            <p>Tilt me. A foil sheen and a moving glare track your pointer in real time.</p>
            <div className="flex justify-between mt-[18px] text-xs font-bold text-[#c7c7dd]"><span>#0042</span><span>★ Rare</span></div>
          </div>
        </article>
      </div>
    </>
  );
}
Vue
<template>
  <div class="gl-stage">
    <article class="gl-card" id="glCard">
      <div class="gl-foil" aria-hidden="true"></div>
      <div class="gl-glare" aria-hidden="true"></div>
      <div class="gl-body">
        <span class="gl-chip">Holographic</span>
        <h3>Founders Edition</h3>
        <p>Tilt me. A foil sheen and a moving glare track your pointer in real time.</p>
        <div class="gl-foot"><span>#0042</span><span>★ Rare</span></div>
      </div>
    </article>
  </div>
</template>

<script setup>
import { onMounted } from 'vue';

let card;
let foil;

onMounted(() => {
  card = document.getElementById('glCard');
  foil = card.querySelector('.gl-foil');
  card.addEventListener('pointermove', function (e) {
    var r = card.getBoundingClientRect();
    var px = (e.clientX - r.left) / r.width;    // 0..1
    var py = (e.clientY - r.top) / r.height;
  
    // 3D tilt toward the cursor (max ~14deg each axis).
    var rx = (0.5 - py) * 28;
    var ry = (px - 0.5) * 28;
    card.style.transform = 'rotateX(' + rx.toFixed(2) + 'deg) rotateY(' + ry.toFixed(2) + 'deg)';
  
    // Glare hotspot follows the pointer.
    card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
    card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
  
    // Shift the foil gradient so the holographic colors flow as you move.
    foil.style.backgroundPosition = (px * 100).toFixed(1) + '% ' + (py * 100).toFixed(1) + '%';
  });
  card.addEventListener('pointerleave', function () {
    card.style.transform = 'rotateX(0) rotateY(0)';
  });
});
</script>

<style scoped>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#07070f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}

.gl-stage{perspective:900px}
.gl-card{position:relative;width:280px;height:380px;border-radius:20px;background:linear-gradient(160deg,#1b1b30,#0d0d1a);border:1px solid #2a2a45;overflow:hidden;transform-style:preserve-3d;transition:transform .12s ease-out;cursor:pointer;box-shadow:0 30px 60px -28px rgba(0,0,0,.8)}

.gl-foil{position:absolute;inset:0;opacity:.0;background:conic-gradient(from 0deg,#ff0080,#7928ca,#2afadf,#ffea00,#ff0080);background-size:200% 200%;mix-blend-mode:color-dodge;transition:opacity .3s;pointer-events:none}
.gl-card:hover .gl-foil{opacity:.35}

.gl-glare{position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,.55),transparent 45%);opacity:0;transition:opacity .25s;mix-blend-mode:soft-light}
.gl-card:hover .gl-glare{opacity:1}

.gl-body{position:relative;z-index:1;height:100%;display:flex;flex-direction:column;padding:24px;transform:translateZ(40px)}
.gl-chip{align-self:flex-start;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;background:rgba(122,40,202,.25);border:1px solid rgba(122,40,202,.5);color:#d8b4fe;padding:5px 11px;border-radius:999px}
.gl-body h3{margin-top:auto;font-size:24px;font-weight:900;letter-spacing:-.02em}
.gl-body p{font-size:13px;color:#a9a9c5;line-height:1.55;margin-top:8px}
.gl-foot{display:flex;justify-content:space-between;margin-top:18px;font-size:12px;font-weight:700;color:#c7c7dd}
</style>
Angular
// @ts-nocheck
// Note: vanilla JS DOM manipulation is preserved as-is inside ngAfterViewInit().
// For idiomatic Angular, replace document.getElementById() with @ViewChild() refs
// and move state into component properties with two-way binding.
import { Component, AfterViewInit, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-glare-card',
  standalone: true,
  imports: [CommonModule],
  encapsulation: ViewEncapsulation.None,
  template: `
    <div class="gl-stage">
      <article class="gl-card" id="glCard">
        <div class="gl-foil" aria-hidden="true"></div>
        <div class="gl-glare" aria-hidden="true"></div>
        <div class="gl-body">
          <span class="gl-chip">Holographic</span>
          <h3>Founders Edition</h3>
          <p>Tilt me. A foil sheen and a moving glare track your pointer in real time.</p>
          <div class="gl-foot"><span>#0042</span><span>★ Rare</span></div>
        </div>
      </article>
    </div>
  `,
  styles: [`
    *{box-sizing:border-box;margin:0;padding:0}
    body{font-family:system-ui,-apple-system,sans-serif;background:#07070f;color:#fff;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}
    
    .gl-stage{perspective:900px}
    .gl-card{position:relative;width:280px;height:380px;border-radius:20px;background:linear-gradient(160deg,#1b1b30,#0d0d1a);border:1px solid #2a2a45;overflow:hidden;transform-style:preserve-3d;transition:transform .12s ease-out;cursor:pointer;box-shadow:0 30px 60px -28px rgba(0,0,0,.8)}
    
    .gl-foil{position:absolute;inset:0;opacity:.0;background:conic-gradient(from 0deg,#ff0080,#7928ca,#2afadf,#ffea00,#ff0080);background-size:200% 200%;mix-blend-mode:color-dodge;transition:opacity .3s;pointer-events:none}
    .gl-card:hover .gl-foil{opacity:.35}
    
    .gl-glare{position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,.55),transparent 45%);opacity:0;transition:opacity .25s;mix-blend-mode:soft-light}
    .gl-card:hover .gl-glare{opacity:1}
    
    .gl-body{position:relative;z-index:1;height:100%;display:flex;flex-direction:column;padding:24px;transform:translateZ(40px)}
    .gl-chip{align-self:flex-start;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;background:rgba(122,40,202,.25);border:1px solid rgba(122,40,202,.5);color:#d8b4fe;padding:5px 11px;border-radius:999px}
    .gl-body h3{margin-top:auto;font-size:24px;font-weight:900;letter-spacing:-.02em}
    .gl-body p{font-size:13px;color:#a9a9c5;line-height:1.55;margin-top:8px}
    .gl-foot{display:flex;justify-content:space-between;margin-top:18px;font-size:12px;font-weight:700;color:#c7c7dd}
  `]
})
export class GlareCardComponent implements AfterViewInit {
  ngAfterViewInit(): void {
    var card = document.getElementById('glCard');
    var foil = card.querySelector('.gl-foil');
    
    card.addEventListener('pointermove', function (e) {
      var r = card.getBoundingClientRect();
      var px = (e.clientX - r.left) / r.width;    // 0..1
      var py = (e.clientY - r.top) / r.height;
    
      // 3D tilt toward the cursor (max ~14deg each axis).
      var rx = (0.5 - py) * 28;
      var ry = (px - 0.5) * 28;
      card.style.transform = 'rotateX(' + rx.toFixed(2) + 'deg) rotateY(' + ry.toFixed(2) + 'deg)';
    
      // Glare hotspot follows the pointer.
      card.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
      card.style.setProperty('--my', (py * 100).toFixed(1) + '%');
    
      // Shift the foil gradient so the holographic colors flow as you move.
      foil.style.backgroundPosition = (px * 100).toFixed(1) + '% ' + (py * 100).toFixed(1) + '%';
    });
    
    card.addEventListener('pointerleave', function () {
      card.style.transform = 'rotateX(0) rotateY(0)';
    });
  }
}