---
name: card-shine-effect
description: Use when you want "Premium, polished, commercial" - Moves a specular shine across a card surface.
---

# Card Shine Effect

> **Category:** Card / Layout  -  **Personality:** Premium, polished, commercial
>
> **Best use:** Credit-card UI, pricing, CTAs
>
> **Ratings:** Professional 5/5 - Casual 3/5 - Exotic 3/5 - Visual impact 4/5 - Difficulty 2/5 - Performance cost 2/5

## What it is

A glossy card surface that **catches the light**: a bright specular band sweeps diagonally across it on hover, a soft white highlight tracks the pointer, and a faint holographic foil shifts colour as the card tilts in 3D. It is the standard treatment for a premium "metal" credit / membership card, a featured pricing tier, or a hero CTA — anything that should feel physical and expensive. The whole thing is layered gradients with `mix-blend-mode`; the sweep is pure CSS, and ~30 lines of vanilla JS add the pointer-tracked specular and tilt.

## Dependencies / CDN

**None — pure CSS effect + tiny vanilla JS.** The demo only loads display/body fonts (optional):

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&family=Unbounded:wght@600;700;800&display=swap" rel="stylesheet">
```

## HTML

Three stacked, `pointer-events:none` layers above the card background, with the content on top:

```html
<div class="cs-stack">
  <div class="cs-back" aria-hidden="true"></div>            <!-- optional fanned card for depth -->
  <article class="cs-card" aria-label="VANTAGE Obsidian card, ending 7204">
    <div class="cs-holo"  aria-hidden="true"></div>          <!-- iridescent holographic foil -->
    <div class="cs-gloss" aria-hidden="true"></div>          <!-- pointer-tracked specular highlight -->
    <div class="cs-sweep" aria-hidden="true"></div>          <!-- the shine band that sweeps across -->
    <div class="cs-face">
      <div class="cs-top">
        <div><span class="cs-cardbrand">VANTAGE</span><span class="cs-tier">Obsidian Reserve</span></div>
        <!-- contactless icon … -->
      </div>
      <div class="cs-mid"><span class="cs-chip"></span><div class="cs-pan">5413  4002  9914  7204</div></div>
      <div class="cs-bottom"><!-- holder name / valid thru / network mark --></div>
    </div>
  </article>
</div>
```

## CSS

```css
/* The card: a clipped, gpu-friendly surface. container-type lets the face
   scale with the card via cqw units; the tilt is driven by CSS vars. */
.cs-card{
  position:relative; width:100%; aspect-ratio:1.586/1;   /* ISO 7810 card ratio */
  border-radius:20px; overflow:hidden; container-type:inline-size; will-change:transform;
  transform:rotateX(var(--ry,0deg)) rotateY(var(--rx,0deg));
  transition:transform .45s cubic-bezier(.22,.61,.36,1), box-shadow .45s ease;
  background:linear-gradient(135deg,#262732 0%,#16171e 40%,#0c0d12 72%,#1b1c24 100%);
  border:1px solid rgba(255,255,255,.09);
  box-shadow:0 34px 64px -22px rgba(0,0,0,.85), 0 1px 0 rgba(255,255,255,.07) inset;
}
.cs-card::before{                                        /* brushed-metal micro texture */
  content:""; position:absolute; inset:0; z-index:1; opacity:.5; pointer-events:none;
  background:repeating-linear-gradient(125deg,rgba(255,255,255,.045) 0 1px,transparent 1px 4px);
}

.cs-holo,.cs-gloss{position:absolute; inset:0; border-radius:inherit; pointer-events:none}

/* 1) HOLOGRAPHIC FOIL — a rainbow that drifts with the pointer (--hx/--hy). */
.cs-holo{
  z-index:2; mix-blend-mode:color-dodge; opacity:.08; transition:opacity .45s ease;
  background:repeating-linear-gradient(110deg,
    hsla(350,95%,62%,.55) 0%, hsla(45,95%,60%,.55) 14%, hsla(120,80%,55%,.55) 28%,
    hsla(190,90%,58%,.55) 42%, hsla(255,90%,66%,.55) 56%, hsla(320,90%,64%,.55) 70%, hsla(350,95%,62%,.55) 84%);
  background-size:260% 260%; background-position:var(--hx,50%) var(--hy,50%);
  -webkit-mask:linear-gradient(125deg,transparent 8%,#000 38%,#000 62%,transparent 92%);
          mask:linear-gradient(125deg,transparent 8%,#000 38%,#000 62%,transparent 92%);
}
/* 2) SPECULAR HIGHLIGHT — a white blob that follows the pointer (--mx/--my). */
.cs-gloss{
  z-index:3; mix-blend-mode:screen; opacity:0; transition:opacity .4s ease;
  background:radial-gradient(46% 46% at var(--mx,50%) var(--my,50%),
            rgba(255,255,255,.5), rgba(255,255,255,.11) 32%, transparent 56%);
}
/* 3) THE SWEEP — a bright diagonal band parked off-card, animated across on hover. */
.cs-sweep{
  position:absolute; top:-32%; bottom:-32%; left:-58%; width:46%; z-index:4;
  pointer-events:none; filter:blur(1.5px); opacity:0; mix-blend-mode:overlay;
  transform:translateX(-240%) rotate(9deg);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.08) 28%,
            rgba(255,255,255,.92) 50%,rgba(255,255,255,.08) 72%,transparent);
}

.cs-card:hover{box-shadow:0 42px 80px -22px rgba(0,0,0,.9),0 0 0 1px rgba(230,205,154,.22) inset}
.cs-card:hover .cs-holo{opacity:.19}
.cs-card:hover .cs-gloss{opacity:1}
.cs-card:hover .cs-sweep,
.cs-card.cs-intro .cs-sweep{animation:cs-sweepmove 1s cubic-bezier(.22,.61,.36,1)}

@keyframes cs-sweepmove{
  0%{transform:translateX(-240%) rotate(9deg); opacity:0}
  10%{opacity:1} 90%{opacity:1}
  100%{transform:translateX(560%) rotate(9deg); opacity:0}
}

@media (prefers-reduced-motion:reduce){
  .cs-card{transform:none!important}
  .cs-card .cs-gloss{opacity:.55}                         /* keep a soft static sheen */
  .cs-card:hover .cs-sweep,.cs-card.cs-intro .cs-sweep{animation:none}
}
```

## JavaScript

Optional — the sweep runs on `:hover` with no JS. This adds the pointer-tracked specular, holographic drift and 3D tilt, plus a one-time intro sweep. It writes CSS custom properties inside a `requestAnimationFrame` so pointer events never thrash layout.

```js
(function(){
  var card = document.querySelector('.cs-card');
  if(!card) return;
  if(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return;

  var raf = 0, p = null;
  function paint(){
    raf = 0; if(!p) return;
    var x = p.x, y = p.y;
    card.style.setProperty('--mx',(x*100).toFixed(1)+'%');   // specular + holo centre
    card.style.setProperty('--my',(y*100).toFixed(1)+'%');
    card.style.setProperty('--hx',(x*100).toFixed(1)+'%');
    card.style.setProperty('--hy',(y*100).toFixed(1)+'%');
    card.style.setProperty('--rx',((x-.5)*15).toFixed(2)+'deg');   // rotateY
    card.style.setProperty('--ry',((.5-y)*11).toFixed(2)+'deg');   // rotateX
  }
  card.addEventListener('pointermove',function(e){
    var r = card.getBoundingClientRect();
    p = { x:Math.min(1,Math.max(0,(e.clientX-r.left)/r.width)),
          y:Math.min(1,Math.max(0,(e.clientY-r.top)/r.height)) };
    if(!raf) raf = requestAnimationFrame(paint);
  });
  card.addEventListener('pointerleave',function(){
    p = null;
    ['--mx','--my','--hx','--hy'].forEach(function(k){card.style.setProperty(k,'50%');});
    card.style.setProperty('--rx','0deg'); card.style.setProperty('--ry','0deg');
  });

  // one-time intro sweep so the effect announces itself
  setTimeout(function(){
    card.classList.add('cs-intro');
    card.addEventListener('animationend',function h(){
      card.classList.remove('cs-intro'); card.removeEventListener('animationend',h);
    });
  },520);
})();
```

## How it works

- **The sweep** is one `linear-gradient` band, narrow and bright in the middle, rotated `9deg` and parked far to the left (`translateX(-240%)`). On hover a keyframe slides it fully across (`translateX(560%)`); `mix-blend-mode:overlay` makes it *brighten* the dark surface instead of painting flat white, so it reads as light raking across glass. The card's `overflow:hidden` clips it to the rounded shape.
- **The specular highlight** (`.cs-gloss`) is a radial gradient whose centre is the live pointer position (`--mx/--my`). `mix-blend-mode:screen` turns it into a believable hot-spot on the dark metal that moves under your cursor.
- **The holographic foil** is a rainbow `repeating-linear-gradient` sized larger than the card; nudging its `background-position` with the pointer makes the colours flow, and a soft `mask` keeps it to a central diagonal stripe. `color-dodge` gives that thin iridescent glint.
- **The tilt** is just `rotateX`/`rotateY` from two more vars, with `perspective` on the parent — it sells the "physical card" illusion and makes the moving highlight feel like real reflection.
- **`container-type:inline-size`** lets every label, the chip and the card number be sized in `cqw`, so the whole face scales perfectly when the card shrinks on mobile.

## Customization

- **Sweep speed / shape:** change the `cs-sweepmove` duration, the band `width` (thinner = sharper glint), and the `rotate()` angle. Drop `filter:blur()` for a hard edge.
- **Repeat on hover:** the demo sweeps once per hover. For a continuous shimmer add `infinite` to the animation (use sparingly — it can look cheap).
- **Card material:** swap the `.cs-card` background gradient — obsidian (shown), brushed steel (`#3a3b44 → #15161c`), or a coloured tier. Raise `.cs-holo` opacity for a full holographic card; drop it to 0 for plain gloss.
- **Highlight strength:** `screen` is punchy; switch `.cs-gloss` to `soft-light` for a subtler satin sheen. Tune the inner-white alpha.
- **Tilt amount:** the `*15` / `*11` multipliers in the JS. Set to 0 to keep it flat and shine-only.

## Accessibility & performance

- Honour **`prefers-reduced-motion`**: the JS `return`s early (no tilt, no pointer tracking, no intro), and CSS sets `transform:none`, disables the sweep, and leaves a single static centred sheen.
- Only **transform / opacity / background-position** animate — all compositor-friendly. The `requestAnimationFrame` coalesces `pointermove` to one write per frame; `will-change:transform` hints the GPU. Cost is low even with the blend layers.
- Decorative layers are `aria-hidden` and `pointer-events:none`; keep the real content (`.cs-face`) as readable text with adequate contrast — never bake the card number into the shine layers.
- The effect is hover/pointer driven, so it's a progressive enhancement: with no pointer (touch / keyboard) the card is still a perfectly legible static card.

## Gotchas

- **`mix-blend-mode` needs a stacking context to blend against.** It blends with whatever is *behind it in the same context* — here the card background and the layers below. If the card sits on a pure-white page the `screen`/`overlay` highlights will barely show; the effect wants a **dark, glossy** surface.
- **`overflow:hidden` is doing the clipping**, so don't also put `transform-style:preserve-3d` on the card (the two fight and can flatten/leak the layers). Keep `perspective` on the *parent*, rotate the card itself.
- **Order matters:** `.cs-sweep` must sit above `.cs-holo`/`.cs-gloss` (higher `z-index`) but below the content (`.cs-face`), or the band will wash out your text.
- **`color-dodge` blows out fast** — past ~0.25 opacity the foil turns into a neon wash and stops reading as "premium." Keep it subtle.
- A CSS `:hover` animation only restarts when hover begins again, so it naturally replays on each re-hover; the JS `cs-intro` class reuses the same keyframe for the initial auto-demo and removes itself on `animationend`.
