How it works
The effect is three layers stacked in one card, and JavaScript does almost nothing. It only writes the pointer's position into CSS variables:
.holo__foil {
background-position: var(--x) var(--y);
mix-blend-mode: color-dodge;
}The layers
- The image, as a normal
<img>. - The foil, a repeating rainbow gradient that's twice the size of the card. Moving its
background-positionslides the colours across as the card turns, which is what makes it look like the surface is catching light. - The glare, a radial gradient centred on the pointer, bright in the middle and dark at the far corners.
Why the blend modes matter
A rainbow painted over a picture just looks like a coloured sheet. Blend modes make it react to what's underneath:
color-dodgeon the foil brightens the image based on the colour above it. Dark areas stay dark and light areas flare up in colour, so the shine follows the shapes in the picture.overlayon the glare lightens or darkens without flattening the image, which keeps its contrast.
isolation: isolate on the card keeps both blends inside it. Without it they'd also mix with whatever is behind the card on the page.
Patterns
The patterns sit above the rainbow as another background layer. A dark line lets almost nothing through to color-dodge, so thin dark lines cut the foil into fine stripes, and tiny white dots turn into bright glitter.
Tilt without the jitter
The card rotates in 3D, so its box on the screen changes as it moves. If you measure the rotating element, the numbers change because of the rotation itself, and the card shakes near its edges. The fix is two elements: an outer one that never moves is measured and holds perspective, and the inner one is rotated.
Two transition speeds make it feel physical: a very short one while following the pointer, and a long one when the pointer leaves, so the card settles back.
On phones
The cursor's job is taken over by the gyroscope through the deviceorientation event. It only works on HTTPS, and iOS also requires DeviceOrientationEvent.requestPermission() to be called from inside a tap, which is why the generated code waits for the first tap on the card.
Using it well
- One at a time. Blending is repainted on every move, so a grid of 50 of these will struggle. Activate only the card being hovered, as this code does.
- Choose dark or mid-tone images.
color-dodgehas little left to brighten in an image that's already light. - Respect reduced motion. The CSS output turns the 3D tilt off under
prefers-reduced-motion.