044

Holo Foil Card

£5

Trading-card holographic tilt: one pointer drives 3D rotation, an iridescent foil sheen, twinkling glitter, depth parallax and a specular bloom, then settles elastically.

advanced
6 more details
3d-transformspointer-effectsblend-modehover-effectvelocity-reactivemicro-interaction
Holo Foil Card - GSAP animation effect preview

This demo reads better at your own screen size than in the frame below:

About this effect

A card that behaves like real holographic foil. Move a pointer across it and five things answer the same gesture at once: the card tilts in 3D, a prismatic sheen slides across its face and shifts hue with the angle, a glitter layer twinkles on its own clock, the art and the type separate into depth, and a specular highlight tracks the light.

Read the full effect overview

Whip the pointer across it and the extreme state opens up: the sheen sweeps hard, the tilt overshoots past the pointer, and the glare blooms out to a blown highlight. Let go and it does not snap; it settles back to its resting pose with a single elastic overshoot.

The foil is CSS, not a filter: layered gradients under color-dodge, overlay and soft-light, positioned by custom properties that GSAP quickSetters write. Three recipes ship (rainbow prism, brushed chrome, cracked ice) and writing a fourth is one background rule.

What's included

10 items
  • Five channels on one input: 3D tilt, angle-dependent iridescent sheen, independently twinkling glitter, art-versus-type depth parallax, and a specular glare that blooms only near grazing angles
  • Real material layering rather than a hue-rotate: prismatic gradients under color-dodge, a specular under overlay, a laminate bevel under soft-light, and a screen-blend bloom that stays shut until it is earned
  • Whip response with velocity lead, so a hard flick makes the tilt overshoot past the pointer and the highlight blow out, then recover
  • Elastic settle back to a per-card resting pose, defined in CSS so the card looks identically lit with JS, without JS, and under reduced motion
  • Three shipped foil recipes (rainbow prism, brushed chrome, cracked ice), each a different material; a new one is a single background rule on a data-foil value
  • Touch drag as a first-class channel with pointer capture and pan-y, so a phone visitor tilts the card and can still scroll the page
  • Device orientation as an optional enhancement, with the iOS permission prompt behind a button that only appears where it is needed, and a silent fallback when it is denied or unavailable
  • Attach to any card-shaped element with data-foil-card; tilt range, gloss strength and hue swing are per-card data attributes
  • Keyboard accessible: a focused card plays the full sweep and arrow keys steer the tilt, with a focus ring that mirrors the hover lift
  • Runs on GSAP core with no plugins, no canvas and no WebGL; the ticker only turns over while a card is actually being touched or settling

Perfect for

5 use cases
  • Collectible, membership and loyalty cards where the card itself is the product
  • Pricing and plan cards that want the premium tier to feel physically different from the others
  • Ticket, pass and NFT-style reveals where a holographic finish signals rarity
  • Music, film and game merch pages showing off a foil-stamped edition
  • Portfolio and agency hero sections needing one tactile, clip-worthy object above the fold

How it works

4 sections

The card's whole material is CSS custom properties. A stack of absolutely positioned layers inside the card carries the prismatic grating, the glitter field, the specular and the bloom, each with its own blend mode, and every one of them is positioned or scaled by a variable. GSAP quickSetters write those variables plus the rotation, so a frame costs a handful of property writes and no style recalculation of the layout.

Pointer position sets the target tilt, but pointer velocity is added on top as a lead angle: the card aims past where the pointer actually is, in proportion to how fast it is travelling. When the flick stops, the lead collapses and the follow tween pulls the card back through centre, which is the overshoot. Speed also sets an energy value that decays on its own clock; energy is what opens the bloom, so a slow drag to the same angle never blows the highlight out.

The grating slides several times faster than the card turns and its hue rotates with the angle, which is what stops the foil reading as a picture printed on the card. The glitter parallaxes faster still through a masked band that drifts on an independent loop, so it twinkles even while the card is held still.

Release runs an elastic.out tween back to the resting angle, which is read from the CSS --rest-x and --rest-y properties rather than assumed to be flat. That is why a card composed at a tilt returns to exactly the pose the stylesheet drew, and why the no-JS and reduced-motion renders are the same composed, lit object.

Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, 15 September 2026. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Holo Foil Card opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£5 Standard license, unlimited projects

Browse free effects

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

2. Add before closing </body> tag:

Code snippet omitted: it ships with the download.

3. Add the card structure to your <body>:

Code snippet omitted: it ships with the download.

The script finds every [data-foil-card] and wires up pointer, touch, keyboard and (where available) device orientation. It writes nothing but custom properties and one rotation, so all the visual work stays in your stylesheet.

Using It With Your Own Design

What the effect needs from your markup: one element carrying data-foil-card with a rotating child carrying data-foil-inner. That is the hard requirement. Everything else is optional and degrades quietly: the two parallax hooks (data-foil-parallax="back" and data-foil-parallax="front") are skipped if absent, and so are any foil layers you leave out. The layer order in the DOM is the layer order on screen, and it is a deliberate choice: sheen and sparkle sit below your content so type stays readable, while glare and bloom sit above it so a hard sweep washes over the whole card the way a real gloss laminate does. Move your content block above the sheen and the foil will paint over your type; that is a valid look, just decide it on purpose.

The card needs a real size. [data-foil-card] ships with width: var(--w) and aspect-ratio: 5 / 7; set --w per card, or replace both rules with your own sizing. Nothing in the script measures your art, only the card's own box.

What is only the demo's styling: everything under the "DEMO: THE DISPLAY CASE" and "DEMO: CARD CONTENT" banners in style.css. That is the body::before / body::after lighting, the .rack grid, the .rack-slot rotations and floor pools, the per-slot --art-base gradients, and all the trading-card furniture (.card-top, .card-name, .card-type, .card-chip, .card-serial). Delete all of it. The three foil recipes under "FOIL RECIPES" are worth keeping even if you rename them: each is one background-image on a [data-foil="..."] selector, and adding a fourth material is one more rule, no JavaScript.

CSS the effect depends on: the "HOLO FOIL CARD - EFFECT CONTRACT" section has to survive a reskin.

  • isolation: isolate on .foil-card__inner. Without it the color-dodge layers punch straight through the card and bleach whatever is behind it on the page. This is the single most common thing to break when moving the card onto a light background.
  • perspective on [data-foil-card] and overflow: hidden + border-radius on .foil-card__inner. The rotation happens on the inner; the perspective must live on its parent.
  • The blend modes themselves. color-dodge on the sheen and sparkle, overlay on the glare, screen on the bloom, soft-light on the frame. Swap them freely for a different material, but a stack that is all normal is not foil, it is a picture of foil.
  • touch-action: pan-y on [data-foil-card]. Drop it and a phone visitor whose finger lands on a card cannot scroll past it; set it to none and you get two-axis drag at the cost of trapping the page scroll.
  • The full set of custom property defaults in the [data-foil-card] block. Those defaults are the no-JS and reduced-motion rendering. They are chosen so a card with no script at all is a composed, lit object rather than a flat rectangle, which is why nothing here is pre-hidden and there is no .has-js gate to restore.
  • --rest-x / --rest-y. The script reads them at init and settles back to them, so a card composed at an angle returns to exactly the pose your stylesheet drew. Leave them at 0deg for a card that rests flat.
  • The overscan: inset: -7% on .foil-card__art and inset: -6% on .foil-layer. The depth parallax slides those layers, and without the bleed you get a bare edge at full tilt.

Options

Per-card data attributes on the [data-foil-card] element.

Attribute Values Default Description
data-foil-card marker required Makes the element a foil card
data-foil any name you style none Selects a foil recipe. prism, brush and ice ship; a new one is a [data-foil="yours"] .foil-layer--sheen background rule
data-foil-tilt degrees 16 Maximum tilt. A whip can lead past this by up to 60%. Above about 22 the card starts reading as a flap rather than a held object
data-foil-gloss number 1 Multiplier on every light channel at once. 0.6 for a restrained card, 1.3 for a loud one
data-foil-hue degrees 130 How far the spectrum rotates across the full tilt range. 0 gives a fixed-colour foil that still moves

CSS custom properties you set (as opposed to the ones the script writes):

Property Default Description
--rest-x 0deg Resting X rotation. The card settles here, and renders here without JS
--rest-y 0deg Resting Y rotation
--w 280px Card width; height follows from aspect-ratio
--art-base dark gradient Background of the art plate behind your artwork

Structural hooks the script looks for, all documented and all optional except the first:

Hook Description
[data-foil-inner] The element that rotates. Required
[data-foil-parallax="back"] Slides with the tilt: the layer that should feel behind the glass
[data-foil-parallax="front"] Slides against it: the layer that should feel in front
[data-foil-gyro-toggle] A button anywhere on the page, hidden by default. Optional. On devices that gate orientation behind a permission prompt it appears as the prompt trigger; once readings are flowing it relabels itself to Recentre and resets the neutral pose

What the Script Writes

Every one of these is a custom property on the card element, updated by a quickSetter. Use them anywhere in your own CSS.

Property Range Meaning
--sx, --sy roughly -40% to 140% Foil grating position. Travels several times faster than the card turns
--hue ± data-foil-hue Spectrum rotation, applied as a hue-rotate filter on the sheen
--sheen 01 Foil layer opacity
--gx, --gy -20%120% The light spot. Drives the glare, the bloom and the sheen's mask
--glare 01 Specular opacity
--bloom 01 Blown-highlight opacity. Stays at 0 unless the tilt is steep or the pointer is fast
--kx, --ky pixels Glitter parallax offset
--mx percent Glitter reveal band, drifting on its own loop
--spark 01 Glitter opacity
--lift 01 1 while the card is engaged. Drives the shadow, and anything else you want

Plus rotationX / rotationY on [data-foil-inner] and x / y on the two parallax hooks.

Accessibility

  • Reduced motion: with prefers-reduced-motion: reduce the script wires up nothing at all. The custom property defaults in the contract CSS are a fully lit card at its resting angle, so the reduced render is a composed object rather than a flat or blank one. Nothing is pre-hidden and nothing needs restoring.
  • Keyboard: give each card tabindex="0". Focus plays the same full sweep a tap does, and the arrow keys then steer the tilt directly. Blur runs the elastic settle, exactly like a pointer leaving.
  • Focus visible: the :focus-visible rule pairs the accent ring with the same shadow lift the hover state uses, so keyboard and pointer users get the same affordance.
  • Labels: the card is decorative furniture around real content, so give it role="group" and an aria-label describing the card, or drop both if the content inside already reads correctly on its own. Every foil layer is aria-hidden.
  • Touch: touch-action: pan-y means a vertical swipe scrolls the page and a sideways drag tilts the card. A tap that never moves plays the sweep, so a coarse-pointer visitor always gets a response, and pointercancel releases safely.
  • Device orientation: nothing is requested on load. On platforms that gate the sensor behind a permission prompt, a [data-foil-gyro-toggle] button is revealed and the prompt only happens if it is pressed; a refusal hides the button and changes nothing else, because touch drag was always the primary channel. Where the sensor is ungated it is simply used, and the same button appears as a Recentre control instead.

Device tilt, in detail

The phone's own movement drives all five channels, not just the rotation, so tipping the handset sweeps the foil, shifts the hue and blooms the glare exactly as a pointer does. Four things make it usable rather than merely wired up:

  • Screen rotation is compensated. The sensor reports beta and gamma against the device's frame and never mentions that the screen has been rotated under it, so an uncompensated card tilts sideways when you tip a landscape phone forwards. The axes are rotated by screen.orientation.angle before use.
  • Neutral follows the hand. The rest pose creeps towards however the phone is actually being held, on about a six second time constant. Without it a phone first read lying on a desk leaves every card pinned at full tilt the moment it is picked up. It is slow enough that a deliberate tilt is over long before the drift eats it, and a sustained lean relaxing back to centre is what a real card in the hand does anyway.
  • A deadzone and a low pass. Raw readings at 60Hz visibly shiver in a still hand. Anything under 0.7 degrees off neutral reads as zero.
  • Flicks whip. Angular speed feeds the same energy channel a fast pointer sweep uses, so snapping the phone blows the glare out and the tilt overshoots.

A finger on the card always wins: while a pointer is captured the sensor is ignored, so a drag never fights the gyro.

If the effect is embedded in an iframe, the host page must grant the feature or the sensor stays silent on a cross-origin frame:

Code snippet omitted: it ships with the download.

Performance Notes

  • A frame is a dozen custom-property writes and one transform per active card. No layout is read, and no style is recalculated for the layers because custom properties on the card only repaint its own compositing layers.
  • The ticker is per card and only runs while that card is being touched or is still settling. Idle cards cost nothing.
  • The depth parallax is clamped as a fraction of the card's width, so the same numbers hold for a 140px card and a 400px one.
  • Blend modes and masks are the expensive part, not the JavaScript. Four blended layers on a handful of cards is comfortable; if you are painting a grid of forty, drop the sparkle and bloom layers on the ones that are not the hero.

Dependencies

Required:

  • GSAP 3.12+ (core only, no plugins)

Optional:

  • None. The effect has no scroll mechanic and does not use Lenis.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

The Vault library, plus future additions to the library.