Flip Transition
A two-sided card: the inner element turns 180° around the vertical axis in 0.8 s, in true 3D (preserve-3d, 800 px perspective), and the back shows up in the same place, at the same size as the front. CSS + 0.9 KB of JavaScript, click, Enter or Space, target side at once when the user reduces motion.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Transitions has 52 effects, including 9 free. Explore the category →
3 usage examples



How it works
The .flpt-card (200 × 200 px) holds the perspective (perspective: 800px): it sets the depth seen by the eye. Inside, .flpt-inner keeps its children in 3D space (transform-style: preserve-3d) and carries the transition transform .8s cubic-bezier(.4, 0, .2, 1). The two .flpt-faces are stacked with inset: 0 and backface-visibility: hidden (plus its -webkit- form): a face turned away from the screen is not drawn. The back .flpt-back starts already flipped (rotateY(180deg)): at rest, you see the front.
When .active toggles, .flpt-inner turns to rotateY(180deg): the front turns away and disappears, the back now faces the screen. Measured in Chromium 151: 250 ms after the click, the card is only 79 px wide on screen (95 px in Firefox 153), almost edge-on; at 1.1 s it has completed its half turn. Checked by screen capture at the card's center, in Chromium, Firefox and WebKit 26.5: front color at rest, back color after the click, front again after the second click — the hidden side never shows mirrored. State B holds until the next press; the second press flips the card back the other way.
The script adds or removes .active on click, Enter or Space on every .flpt-card of the page, keeps aria-pressed up to date and sets aria-hidden on the side facing away. The sides also carry a visibility switch delayed by 0.27 s, the measured moment when the card is edge-on (90°): each side is only drawn during its half of the turn. It is a safety belt for engines that do not apply backface-visibility inside preserve-3d — such as Playwright's test WebKit, which without it shows the back mirrored at rest. Everything is prefixed flpt-, nothing is exposed on window. The perspective sits on the card, not on the page: each card keeps its own vanishing point at its own center, even when lined up with others. A variant that flips several cards in sequence also exists in the category.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, the rotation is removed and the target side shows at once (measured at 60 ms in Chromium and Firefox: rotation already at 180°). A 3D rotation is a large motion, often poorly tolerated by people prone to dizziness. - Keyboard and screen readers: the card has
role="button",tabindex="0"andaria-pressed; Enter and Space flip it like a click (measured). The back-facing side getsaria-hidden="true": the screen reader only reads the visible side. 2 px outline on keyboard focus. - Say there is a back: nothing tells users a card flips. Keep a hint ("See the back", a flip icon) or Front / Back buttons next to it.
- No link on the hidden side unless you take it out of the keyboard path: add
inertto the back side while it faces away, otherwise Tab may stop on an invisible element.
Browser compatibility
CSS 3D (perspective, transform-style: preserve-3d, backface-visibility with the -webkit- prefix, rotateY) + 0.9 KB of JavaScript, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26.5: rotation, correct side checked by screen capture at rest, after the click and after the return, state B held; keyboard and reduced motion measured in Chromium and Firefox. The test WebKit does not apply backface-visibility under preserve-3d: the code's visibility switch is what keeps the front clean there. Real Safari was not measured; check on an iPhone before a launch that matters.
Without JavaScript, the front stays on screen. Without 3D support, the card switches sides with no visible rotation, thanks to the visibility switch. Without inset, write top: 0; right: 0; bottom: 0; left: 0 on .flpt-face.
The code
HTML structure to paste into your page (the CSS + JS come with Premium):
<div class="flpt-stage">
<div class="flpt-card" role="button" tabindex="0" aria-pressed="false">
<div class="flpt-inner">
<div class="flpt-face flpt-front">…</div>
<div class="flpt-face flpt-back" aria-hidden="true">…</div>
</div>
</div>
<span class="flpt-hint">…</span>
</div>
The full HTML + CSS + JS of Flip Transition, ready to paste — plus all 840 effects. Or $299 once, lifetime access.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Perspective (CSS — perspective on .flpt-card) |
800 px | The smaller the value, the more the card seems to come toward you as it turns: 500 px gives a strong effect, 1,200 px an almost flat turn. Use about 3 to 4 times the card width. |
Axis (CSS — rotateY) |
vertical axis | rotateX(180deg) on .flpt-inner and on .flpt-back flips the card top to bottom, like a desk calendar. |
Duration and curve (CSS — transition on .flpt-inner) |
0.8 s, cubic-bezier(.4, 0, .2, 1) |
0.6 s for a snappy gesture; cubic-bezier(.3, 1.4, .5, 1) adds a slight overshoot on landing. Also change the visibility delay (0.27 s, .flpt-front, .flpt-back rule), set to the moment the card is edge-on: 0.2 s for 0.6 s, 0.1 s with the overshoot curve; otherwise a side shows from the back for 50 to 150 ms. |
Card size (CSS — .flpt-card) |
200 × 200 px | Give it the real object's proportions: 340 × 214 px for a bank or business card. Both sides follow, they use inset: 0. |
Sides (CSS — .flpt-front, .flpt-back) |
indigo gradient and brick gradient | Keep opaque backgrounds on both sides; a transparent one would show the other side during the turn. |
FAQ
perspective on the parent sets one viewpoint for everything inside: the card turns in front of a still eye, and its two sides, in the same 3D space thanks to preserve-3d, distort together. Putting perspective() in the turning element's transform also works for a single element, but shifts the viewpoint on every frame of the animation..flpt-card:hover .flpt-inner { transform: rotateY(180deg) }, .flpt-card:hover .flpt-front { visibility: hidden } and .flpt-card:hover .flpt-back { visibility: visible }. The last two carry over to hover the visibility switch the code applies to .active: without them, the card looks empty in Chromium and shows its front mirrored in Firefox and WebKit (with them, the back is correct in all three, measured). But touch screens have no hover, and a tap often leaves the card stuck on its back; keep the click (or both) as soon as the back holds useful information.