Turning the card

The work section on my landing page is a row of tall cards. The front has a title, a line of summary and a few tags. The back has the details: where, for whom, how big the team was. You click a card (or tap it, or press Enter) and it turns over like a playing card.

The turn was the easy part. Getting people to know the card turns took three goes.

§ 0012019

A newsletter for a small town

One page every Friday, written so the baker and the mayor both read to the end.

WritingPrint
§ 0012019
Setting
A hill town
Readers
About 4,000
Team
Two
§ 0022021

A shop that only sells maps

Signs, labels and a window that changes with the season.

IdentitySignageRetail
§ 0022021
Setting
A corner shop
Visitors
Lost, mostly
Team
Three
§ 0032024

A choir for people who can't sing

Posters, a sign-up page and one very forgiving rehearsal schedule.

CampaignWeb
§ 0032024
Setting
A community hall
Members
Forty, and rising
Team
One
Hover a card, then click it. Scroll it out of view and back and each card peeks again. The chips show what you get with reduced motion, and without JavaScript at all.

The turn

The flip itself is a GSAP tween on the inner wrapper: rotationY to 180° over 0.9 seconds with power3.inOut. The two faces sit in the same grid cell with backface-visibility: hidden, so whichever one points at you is the one you see.

A plain rotation looked like a door swinging. What made it feel like a card was a second tween running alongside: the scale dips to 0.96 at the halfway point and comes back to 1. It's a tiny thing, but it reads as the card being lifted off the table to be turned, rather than spun on a pin.

var CONFIG = {
  flip:  { duration: 0.9, ease: 'power3.inOut', dip: 0.96 },
  hover: { angle: 14, lift: -6, duration: 0.55, iconSpin: 180 },
  peek:  { hold: 1.0, stagger: 0.12, repeat: 'always', threshold: 0.6 },
  reduced: { duration: 0.25 }
};

Every number that shapes the motion lives in that one block at the top of the file, so the motion can be tuned without reading a line of the code underneath it.

Saying it turns

A card that turns is only useful if people try. My first answer was to make each card look like the top of a stack: two thin offset edges peeking out behind it. It looked like a pile of index cards, which is honest, but it said "there are more of these" rather than "this has a back".

Next I tried a turned-up page corner in the bottom right, the old sign for a page you can turn. It lasted about an hour. It looked like a sticker, and it fought with the tags.

The three cues, in the order I tried them. Only the last one stayed.

What stayed was motion instead of decoration. On a mouse, the card starts to turn when you hover it: 14° round, 6px up, a deeper shadow, and the little arrows icon spins half a turn. You can see it wants to go. Clicking finishes the job, from wherever the hover left it.

Touch screens have no hover, so each card plays that same pose for itself as it scrolls into view. It holds for a second, then settles, with neighbours staggered by 0.12s so a row ripples rather than jumps. It only fires once 60% of the card is visible, and it plays every time the card comes back. At first it played once, and only on touch screens. Then once for everyone. Then every time it comes back into view, which is how it ships now.

Small courtesies

  • The button label swaps from Flip to Back at half-time, when the card is edge-on and nobody can see it change.
  • The hidden face is inert and aria-hidden, and the button carries aria-pressed, so a screen reader hears one side at a time and knows which.
  • With reduced motion there's no rotation at all, just a 0.25s fade to the other side.
  • Titles, summaries and tags get equal heights across each row (measured again on resize and when fonts arrive), so every card's text starts on the same line.
  • Without GSAP or JavaScript the cards show both sides, one under the other. Nothing is ever hidden behind a script that didn't load.

One bug worth admitting: on a phone, tapping a card in the middle of its peek left it turned over and tilted. The fix is one line that settles the peek before the flip starts.

Built
October 2026
Stack
GSAP, CSS 3D transforms
Lives at
thomasalex.in, the Work section
Source
assets/work-flip.js

Built with Claude Code. The demo cards are made up; the real ones are on the landing page.